对dubbo的DubboReference.check的参数进行剖析

这篇具有很好参考价值的文章主要介绍了对dubbo的DubboReference.check的参数进行剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

在使用dubbo的时候,发现当消费者启动的时候,如果提供者没有启动,即使提供者后来启动了,消费者也调不通提供者提供的接口了。

注册中心使用都是nacos

dubbo版本是3.0.4

例子

接口

public interface DemoService {
    String sayHello();
}

提供者

@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHello() {
        return "hello";
    }
}

@EnableDubbo
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ReferenceCheckProviderStarter {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ReferenceCheckProviderStarter.class)
                .web(WebApplicationType.NONE) // .REACTIVE, .SERVLET
                .run(args);
        System.out.println("dubbo service started");
    }
}

消费者

@EnableDubbo
@RestController
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ReferenceCheckConsumerStarter {

    @DubboReference
    private DemoService demoService;

    @GetMapping("/dubbo/nacos/test")
    public Object test() {
        return demoService.sayHello();
    }

    public static void main(String[] args) {
        SpringApplication.run(ReferenceCheckConsumerStarter.class, args);
    }
}

1. 先启动provider,再启动consumer

a. 启动provider
对dubbo的DubboReference.check的参数进行剖析
nacos出现provider的服务

b. 启动consumer
对dubbo的DubboReference.check的参数进行剖析
nacos出现consumer的服务

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回

hello

c. 终止provider
对dubbo的DubboReference.check的参数进行剖析
nacos上provider的服务消失了

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回

No provider available from registry

d. 重新启动provider
对dubbo的DubboReference.check的参数进行剖析
nacos出现provider的服务

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回

hello

可以看出:先启动provider,再启动consumer,整个过程是没问题。

2. 先启动consumer,再启动provider

a. 启动consumer
对dubbo的DubboReference.check的参数进行剖析
对dubbo的DubboReference.check的参数进行剖析
nacos出现consumer的服务,但立即又消失了

b. 启动provider
对dubbo的DubboReference.check的参数进行剖析
nacos出现provider的服务

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回

Directory already destroyed .

可以看出:当consumer先启动时,如果provider此时没有启动,consumer就再也访问不到provider的服务了。

3. 先启动consumer,再启动provider (check=false)

修改一下注解@DubboRefere的参数

@DubboReference(check = false)
private DemoService demoService;

a. 启动consumer
对dubbo的DubboReference.check的参数进行剖析
nacos出现consumer的服务

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回
No provider available from registry

b. 启动provider
对dubbo的DubboReference.check的参数进行剖析
nacos出现provider的服务

访问 http://127.0.0.1:8080/dubbo/nacos/test 后返回
hello

可以看出:即使是consumer先启动,当provider启动后,consumer还是能够访问到provider的服务的。

关于报错

org.apache.dubbo.rpc.RpcException: No provider available from registry

public class RegistryDirectory<T> extends DynamicDirectory<T> {
@Override
    public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) {
            // 1. No service provider 2. Service providers are disabled
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
                    getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
                    NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
                    ", please check status of providers(disabled, not registered or in blacklist).");
        }

        // ......
    }
}
public class RegistryDirectory<T> extends DynamicDirectory<T> {
    String EMPTY_PROTOCOL = "empty";

    private void refreshInvoker(List<URL> invokerUrls) {
        Assert.notNull(invokerUrls, "invokerUrls should not be null");

        if (invokerUrls.size() == 1
                && invokerUrls.get(0) != null
                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            this.forbidden = true; // Forbid to access
            this.invokers = Collections.emptyList();
            routerChain.setInvokers(this.invokers);
            destroyAllInvokers(); // Close all invokers
        } else {
            this.forbidden = false; // Allow to access
            
            if (invokerUrls == Collections.<URL>emptyList()) {
                invokerUrls = new ArrayList<>();
            }
            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
                this.cachedInvokerUrls = new HashSet<>();
                this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            
            // can't use local reference because this.urlInvokerMap might be accessed at isAvailable() by main thread concurrently.
            Map<URL, Invoker<T>> oldUrlInvokerMap = null;
            if (this.urlInvokerMap != null) {
                // the initial capacity should be set greater than the maximum number of entries divided by the load factor to avoid resizing.
                oldUrlInvokerMap = new LinkedHashMap<>(Math.round(1 + this.urlInvokerMap.size() / DEFAULT_HASHMAP_LOAD_FACTOR));
                this.urlInvokerMap.forEach(oldUrlInvokerMap::put);
            }
            Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls);// Translate url list to Invoker map

            /**
             * If the calculation is wrong, it is not processed.
             *
             * 1. The protocol configured by the client is inconsistent with the protocol of the server.
             *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
             * 2. The registration center is not robust and pushes illegal specification data.
             *
             */
            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
                logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
                        .toString()));
                return;
            }

            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
            // pre-route and build cache, notice that route cache should build on original Invoker list.
            // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
            routerChain.setInvokers(newInvokers);
            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
            this.urlInvokerMap = newUrlInvokerMap;

            try {
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }

            // notify invokers refreshed
            this.invokersChanged();
        }
    }

    private synchronized void refreshOverrideAndInvoker(List<URL> urls) {
        // mock zookeeper://xxx?mock=return null
        overrideDirectoryUrl();
        refreshInvoker(urls);
    }

    @Override
    public synchronized void notify(List<URL> urls) {
        if (isDestroyed()) {
            return;
        }

        Map<String, List<URL>> categoryUrls = urls.stream()
                .filter(Objects::nonNull)
                .filter(this::isValidCategory)
                .filter(this::isNotCompatibleFor26x)
                .collect(Collectors.groupingBy(this::judgeCategory));

        List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
        this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

        List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
        toRouters(routerURLs).ifPresent(this::addRouters);

        // providers
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());

        // 3.x added for extend URL address
        ExtensionLoader<AddressListener> addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class);
        List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
        if (supportedListeners != null && !supportedListeners.isEmpty()) {
            for (AddressListener addressListener : supportedListeners) {
                providerURLs = addressListener.notify(providerURLs, getConsumerUrl(),this);
            }
        }

        refreshOverrideAndInvoker(providerURLs); // 这里
    }

}
public abstract class AbstractRegistry implements Registry {
    /**
     * Notify changes from the Provider side.
     *
     * @param url      consumer side url
     * @param listener listener
     * @param urls     provider latest urls
     */
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((CollectionUtils.isEmpty(urls))
            && !ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", url size: " + urls.size());
        }
        // keep every provider's category.
        Map<String, List<URL>> result = new HashMap<>(); // 这里
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
                String category = u.getCategory(DEFAULT_CATEGORY);
                List<URL> categoryList = result.computeIfAbsent(category, k -> new ArrayList<>()); // 这里
                categoryList.add(u); // 这里
            }
        }
        if (result.size() == 0) {
            return;
        }
        Map<String, List<URL>> categoryNotified = notified.computeIfAbsent(url, u -> new ConcurrentHashMap<>());
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
            listener.notify(categoryList); // 这里
            // We will update our cache file after each notification.
            // When our Registry has a subscribe failure due to network jitter, we can return at least the existing cache URL.
            if (localCacheEnabled) {
                saveProperties(url);
            }
        }
    }
}
public class NacosRegistry extends FailbackRegistry {
    private void notifySubscriber(URL url, NotifyListener listener, Collection<Instance> instances) {
        List<Instance> enabledInstances = new LinkedList<>(instances);
        if (enabledInstances.size() > 0) {
            //  Instances
            filterEnabledInstances(enabledInstances);
        }
        List<URL> urls = toUrlWithEmpty(url, enabledInstances);
        NacosRegistry.this.notify(url, listener, urls); // 这里
    }

    String EMPTY_PROTOCOL = "empty";

    private List<URL> toUrlWithEmpty(URL consumerURL, Collection<Instance> instances) {
        List<URL> urls = buildURLs(consumerURL, instances);
        if (urls.size() == 0) { // 这里
            URL empty = URLBuilder.from(consumerURL)
                .setProtocol(EMPTY_PROTOCOL)
                .addParameter(CATEGORY_KEY, DEFAULT_CATEGORY)
                .build();
            urls.add(empty);
        }
        return urls;
    }
}

当没有可用的服务时,instances是空的
对dubbo的DubboReference.check的参数进行剖析

当有可用的服务时,instances是不为空的
对dubbo的DubboReference.check的参数进行剖析

是怎么通知的

public class ServiceInfoHolder implements Closeable {
    public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {
        String serviceKey = serviceInfo.getKey();
        if (serviceKey == null) {
            return null;
        }
        ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
        if (isEmptyOrErrorPush(serviceInfo)) {
            //empty or error push, just ignore
            return oldService;
        }
        serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
        boolean changed = isChangedServiceInfo(oldService, serviceInfo);
        if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) {
            serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo));
        }
        MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());
        if (changed) { // 这里
            NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "
                    + JacksonUtils.toJson(serviceInfo.getHosts()));
            NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),
                    serviceInfo.getClusters(), serviceInfo.getHosts())); // 这里
            DiskCache.write(serviceInfo, cacheDir);
        }
        return serviceInfo;
    }
}

public class DefaultPublisher extends Thread implements EventPublisher {
    private BlockingQueue<Event> queue;

    @Override
    public void init(Class<? extends Event> type, int bufferSize) {
        setDaemon(true);
        setName("nacos.publisher-" + type.getName());
        this.eventType = type;
        this.queueMaxSize = bufferSize;
        this.queue = new ArrayBlockingQueue<>(bufferSize); // 这里
        start();
    }

    @Override
    public boolean publish(Event event) {
        checkIsStart();
        boolean success = this.queue.offer(event); // 这里
        if (!success) {
            LOGGER.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event);
            receiveEvent(event);
            return true;
        }
        return true;
    }

    @Override
    public void run() {
        openEventHandler();
    }
    
    void openEventHandler() {
        try {
            
            // This variable is defined to resolve the problem which message overstock in the queue.
            int waitTimes = 60;
            // To ensure that messages are not lost, enable EventHandler when
            // waiting for the first Subscriber to register
            for (; ; ) {
                if (shutdown || hasSubscriber() || waitTimes <= 0) {
                    break;
                }
                ThreadUtils.sleep(1000L);
                waitTimes--;
            }
            
            for (; ; ) {
                if (shutdown) {
                    break;
                }
                final Event event = queue.take(); // 这里
                receiveEvent(event);  // 这里
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Throwable ex) {
            LOGGER.error("Event listener exception : ", ex);
        }
    }

    void receiveEvent(Event event) {
        final long currentEventSequence = event.sequence();
        
        if (!hasSubscriber()) {
            LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.");
            return;
        }
        
        // Notification single event listener
        for (Subscriber subscriber : subscribers) {
            // Whether to ignore expiration events
            if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
                        event.getClass());
                continue;
            }
            
            // Because unifying smartSubscriber and subscriber, so here need to think of compatibility.
            // Remove original judge part of codes.
            notifySubscriber(subscriber, event); // 这里
        }
    }

    @Override
    public void notifySubscriber(final Subscriber subscriber, final Event event) {
        
        LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
        
        final Runnable job = () -> subscriber.onEvent(event);
        final Executor executor = subscriber.executor(); 
        
        if (executor != null) {
            executor.execute(job); // 这里
        } else {
            try {
                job.run(); // 这里
            } catch (Throwable e) {
                LOGGER.error("Event callback exception: ", e);
            }
        }
    }
}

public class InstancesChangeNotifier extends Subscriber<InstancesChangeEvent> {
    @Override
    public void onEvent(InstancesChangeEvent event) {
        String key = ServiceInfo
                .getKey(NamingUtils.getGroupedName(event.getServiceName(), event.getGroupName()), event.getClusters());
        ConcurrentHashSet<EventListener> eventListeners = listenerMap.get(key);
        if (CollectionUtils.isEmpty(eventListeners)) {
            return;
        }
        for (final EventListener listener : eventListeners) {
            final com.alibaba.nacos.api.naming.listener.Event namingEvent = transferToNamingEvent(event);
            if (listener instanceof AbstractEventListener && ((AbstractEventListener) listener).getExecutor() != null) {
                ((AbstractEventListener) listener).getExecutor().execute(() -> listener.onEvent(namingEvent)); // 这里
            } else {
                listener.onEvent(namingEvent); // 这里
            }
        }
    }
}

public class NacosRegistry extends FailbackRegistry {
        @Override
        public void onEvent(Event event) {
            if (event instanceof NamingEvent) {
                NamingEvent e = (NamingEvent) event;
                notifier.notify(e.getInstances()); // 这里
            }
        }
}

public abstract class RegistryNotifier {
    public synchronized void notify(Object rawAddresses) {
        this.rawAddresses = rawAddresses;
        long notifyTime = System.currentTimeMillis();
        this.lastEventTime = notifyTime;

        long delta = (System.currentTimeMillis() - lastExecuteTime) - delayTime;

        // more than 10 calls && next execute time is in the future
        boolean delay = shouldDelay.get() && delta < 0;
        if (delay) {
            scheduler.schedule(new NotificationTask(this, notifyTime), -delta, TimeUnit.MILLISECONDS); // 这里
        } else {
            // check if more than 10 calls
            if (!shouldDelay.get() && executeTime.incrementAndGet() > DEFAULT_DELAY_EXECUTE_TIMES) {
                shouldDelay.set(true);
            }
            scheduler.submit(new NotificationTask(this, notifyTime)); // 这里
        }
    }

    public static class NotificationTask implements Runnable {
        private final RegistryNotifier listener;
        private final long time;

        public NotificationTask(RegistryNotifier listener, long time) {
            this.listener = listener;
            this.time = time;
        }

        @Override
        public void run() {
            try {
                if (this.time == listener.lastEventTime) {
                    listener.doNotify(listener.rawAddresses); // 这里
                    listener.lastExecuteTime = System.currentTimeMillis();
                    synchronized (listener) {
                        if (this.time == listener.lastEventTime) {
                            listener.rawAddresses = null;
                        }
                    }
                }
            } catch (Throwable t) {
                logger.error("Error occurred when notify directory. ", t);
            }
        }
    }}
}

public class NacosRegistry extends FailbackRegistry {

    private class RegistryChildListenerImpl implements EventListener {
        private RegistryNotifier notifier;

        public RegistryChildListenerImpl(String serviceName, URL consumerUrl, NotifyListener listener) {
            notifier = new RegistryNotifier(getUrl(), NacosRegistry.this.getDelay()) {
                @Override
                protected void doNotify(Object rawAddresses) {
                    List<Instance> instances = (List<Instance>) rawAddresses;
                    if (isServiceNamesWithCompatibleMode(consumerUrl)) {
                        /**
                         * Get all instances with corresponding serviceNames to avoid instance overwrite and but with empty instance mentioned
                         * in https://github.com/apache/dubbo/issues/5885 and https://github.com/apache/dubbo/issues/5899
                         */
                        NacosInstanceManageUtil.initOrRefreshServiceInstanceList(serviceName, instances);
                        instances = NacosInstanceManageUtil.getAllCorrespondingServiceInstanceList(serviceName);
                    }
                    NacosRegistry.this.notifySubscriber(consumerUrl, listener, instances); // 这里
                }
            };
        }
}

然后就调用了上面的👆🏻

什么时候添加监听器的?

public class NacosRegistry extends FailbackRegistry {

    private void subscribeEventListener(String serviceName, final URL url, final NotifyListener listener)
        throws NacosException {
        EventListener eventListener = new RegistryChildListenerImpl(serviceName, url, listener);  // 这里
        namingService.subscribe(serviceName,
            getUrl().getGroup(Constants.DEFAULT_GROUP),
            eventListener); // 这里
    }

    private void doSubscribe(final URL url, final NotifyListener listener, final Set<String> serviceNames) {
        try {
            if (isServiceNamesWithCompatibleMode(url)) {
                List<Instance> allCorrespondingInstanceList = Lists.newArrayList();

                /**
                 * Get all instances with serviceNames to avoid instance overwrite and but with empty instance mentioned
                 * in https://github.com/apache/dubbo/issues/5885 and https://github.com/apache/dubbo/issues/5899
                 *
                 * namingService.getAllInstances with {@link org.apache.dubbo.registry.support.AbstractRegistry#registryUrl}
                 * default {@link DEFAULT_GROUP}
                 *
                 * in https://github.com/apache/dubbo/issues/5978
                 */
                for (String serviceName : serviceNames) {
                    List<Instance> instances = namingService.getAllInstances(serviceName,
                        getUrl().getGroup(Constants.DEFAULT_GROUP));
                    NacosInstanceManageUtil.initOrRefreshServiceInstanceList(serviceName, instances);
                    allCorrespondingInstanceList.addAll(instances);
                }
                notifySubscriber(url, listener, allCorrespondingInstanceList); 
                for (String serviceName : serviceNames) {
                    subscribeEventListener(serviceName, url, listener); // 这里
                }
            } else {
                for (String serviceName : serviceNames) {
                    List<Instance> instances = new LinkedList<>();
                    instances.addAll(namingService.getAllInstances(serviceName
                        , getUrl().getGroup(Constants.DEFAULT_GROUP)));
                    String serviceInterface = serviceName;
                    String[] segments = serviceName.split(SERVICE_NAME_SEPARATOR, -1);
                    if (segments.length == 4) {
                        serviceInterface = segments[SERVICE_INTERFACE_INDEX];
                    }
                    URL subscriberURL = url.setPath(serviceInterface).addParameters(INTERFACE_KEY, serviceInterface,
                        CHECK_KEY, String.valueOf(false));
                    notifySubscriber(subscriberURL, listener, instances);
                    subscribeEventListener(serviceName, subscriberURL, listener);
                }
            }
        } catch (Throwable cause) {
            throw new RpcException("Failed to subscribe " + url + " to nacos " + getUrl() + ", cause: " + cause.getMessage(), cause);
        }
    }
}

org.apache.dubbo.rpc.RpcException: Directory already destroyed

public abstract class AbstractDirectory<T> implements Directory<T> {
    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }

        return doList(invocation);
    }

    @Override
    public void destroy() {
        destroyed = true; // 这里
    }
}
public class ReferenceConfig<T> extends ReferenceConfigBase<T> {

    private void checkInvokerAvailable() throws IllegalStateException {
        if (shouldCheck() && !invoker.isAvailable()) {
            invoker.destroy(); // 这里
            throw new IllegalStateException("Should has at least one way to know which services this interface belongs to," +
                " subscription url: " + invoker.getUrl());
        }
    }

    protected synchronized void init() {
        // ......

        checkInvokerAvailable(); // 这里
    }

}
public abstract class ReferenceConfigBase<T> extends AbstractReferenceConfig {
    public boolean shouldCheck() {
        checkDefault();
        Boolean shouldCheck = isCheck(); // 这里
        if (shouldCheck == null && getConsumer() != null) {
            shouldCheck = getConsumer().isCheck(); 
        }
        if (shouldCheck == null) {
            // default true // 这里
            shouldCheck = true;
        }
        return shouldCheck;
    }
}
public class RegistryDirectory<T> extends DynamicDirectory<T> {
    @Override
    public boolean isAvailable() {
        if (isDestroyed() || this.forbidden) { // 这里
            return false;
        }
        Map<URL, Invoker<T>> localUrlInvokerMap = urlInvokerMap; // 这里
        return CollectionUtils.isNotEmptyMap(localUrlInvokerMap)
                && localUrlInvokerMap.values().stream().anyMatch(Invoker::isAvailable);
    }
}

如果没有设置check字段,那么就会在启动的时候检查提供方是否可用,如果不可用,就销毁了。文章来源地址https://www.toymoban.com/news/detail-422887.html

到了这里,关于对dubbo的DubboReference.check的参数进行剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mybatis-Plus+Nacos+Dubbo进行远程RPC调用保姆级教程

    本文通过简单的示例代码和说明,让读者能够了解Mybatis-Plus+Nacos+Dubbo进行远程RPC调用的简单使用  默认你已经看过我之前的教程了,并且拥有上个教程完成的项目, 之前的教程 https://www.cnblogs.com/leafstar/p/17638782.html 项目链接在最后   1.在bank1的pom文件中引入以下依赖   2.使用

    2024年02月12日
    浏览(27)
  • dubbo 服务注册使用了内网IP,而服务调用需要使用公网IP进行调用

    使用dubbo时,提供者注册时显示服务地址ip为[内网IP:20880],导致其他消费者在外部连接的情况下时,调用dubbo服务失败 (1). 先查询一下服务器的hostname (2). 修改hostname解析绑定公网ip值(最后一行),即可 (3)、服务器不用重启,服务提供者重启 重启服务,注意除注册中心端口

    2024年02月09日
    浏览(41)
  • HttpGet请求与Post请求中参数乱码原因剖析与解决方案

    1 Get请求 当向服务器发送请求URL的请求时 :localhost:8080/getinfo?username=张三,通常在服务端中解析username的参数值时会获得一串难以解读的字符信息。 1.1 原因解析 Get请求中的请求参数会拼接在请求URL中,当URL中存在中文时,浏览器会对请求URL进行编码,其中编码的时候使用的

    2024年02月13日
    浏览(37)
  • 使用Beego和MySQL实现帖子和评论的应用,并进行接口测试(附源码和代码深度剖析)

    经过对需求的分析,我增加了一些额外的东西,比如增加了 user 用户,因为我考虑到帖子或者是评论(跟回帖差不多)都会有作者,主要的功能有增加帖子、查看所有或单个帖子、增加评论、查看某个帖子的所有评论或单个评论。数据我是将它存在数据库中,而不是内存中。

    2024年02月14日
    浏览(40)
  • C语言深度剖析,关于查找一个数组里面的最大值(max)、最小值(min)的通俗算法,简单易懂。采用比较法进行查找。

    这里采用了一个假设 假设第一个数为最大值,其他数与第一个数比较。 这个算法与上面求解最大值的方法相反。 1、首先,定义行和列。 用行标记来确定列的数量。 i 来表示行, j来表示列。 2、内嵌的for循环只打印一行所有列。 如,i=3时,此时j=3. 从1*3 遍历到3*3后内嵌循环

    2024年02月08日
    浏览(44)
  • Postman 如何进行参数化

    Postman作为一款接口测试工具,受到了非常多的开发工程师的拥护。 那么做为测试,了解Postman这款工具就成了必要的了。 这篇文章就是为了解决Postman怎么进行参数化的。 全局变量是将这个变量设置成整个程序的都可以用,不用去区分环境设置。 1.打开Postman,点击右上角的环

    2024年02月04日
    浏览(23)
  • 01-将函数参数化进行传递

    项目源码:https://github.com/java8/ 在我们进行开发中,经常需要面临需求的不断变更,我们可以将 行为参数化 以适应不断变更的需求。 行为参数化就是可以帮助我们处理频繁变更的需求的一种软件开发模式 我们可以将代码块作为参数传递给方法。 例如,现有一个仓库,我们想

    2024年02月15日
    浏览(44)
  • python是如何进行参数传递的?

    在分析python的参数传递是如何进行的之前,我们需要先来了解一下,python变量和赋值的基本原理,这样有助于我们更好的理解参数传递。 python变量以及赋值 数值 从几行代码开始 我们先将1赋值给a,也就是a指向了1这个对象, 在python中一切皆对象 。接着b=a,则表示让b也指向

    2024年02月14日
    浏览(46)
  • Spring MVC各种参数进行封装

    目录 一、简单数据类型 1.1 控制器方法 1.2 测试结果 二、对象类型 2.1 单个对象 2.1.1 控制器方法 2.1.2 测试结果 2.2 关联对象 2.2.1 控制器方法 2.2.2 测试结果 三、集合类型 3.1 简单数据类型集合 3.1.1 控制方法 3.1.2 测试结果 3.2 对象数据类型集合 3.2.1 控制器方法 3.2.2 测试结

    2024年02月09日
    浏览(33)
  • 接收来自客户端的参数使用【JSR303校验框架】进行校验参数是否合法

    目录 1:JSR303校验 1.1:统一校验的需求 1.2:统一校验实现 1.3:分组校验 1.4:校验规则不满足? 前端请求后端接口传输参数,是在controller中校验还是在Service中校验? 答案是都需要校验,只是分工不同。 Contoller中校验请求参数的合法性,包括:必填项校验,数据格式校验,

    2023年04月27日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包