服务暴露的入口方法是 ServiceBean 的 onApplicationEvent。onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务导出操作。方法代码如下:

ServiceBean#onApplicationEvent

public void onApplicationEvent(ContextRefreshedEvent event) {
//是不是已经暴露或 是不是被取消
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}

下面我们看看export方法:

public synchronized void export() {
//一开始进来这个是空的
if (provider != null) {
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
//表示是否服务已经暴露
if (export != null && !export) {
return;
}
//是否设置了延迟暴露
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
@Override
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}

这个方法主要是进行校验服务有没有暴露过,有没有设置延迟。

然后进入到doExport方法中:

ServiceConfig#doExport

protected synchronized void doExport() {
//如果已经取消暴露,则直接抛出异常
if (unexported) {
throw new IllegalStateException("Already unexported!");
}
//如果已经暴露过,则返回
if (exported) {
return;
}
exported = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
//如果ProviderConfig为空,则给它进行赋值
checkDefault();
// 下面几个 if 语句用于检测 provider、application 等核心配置类对象是否为空,
// 若为空,则尝试从其他配置类对象中获取相应的实例。
if (provider != null) {
if (application == null) {
application = provider.getApplication();
}
if (module == null) {
module = provider.getModule();
}
if (registries == null) {
registries = provider.getRegistries();
}
if (monitor == null) {
monitor = provider.getMonitor();
}
if (protocols == null) {
protocols = provider.getProtocols();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
//泛型化
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//校验接口里面是否有设置的方法
checkInterfaceAndMethods(interfaceClass, methods);
//校验接口的实现类不能为空
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class<?> localClass;
try {
localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not " +
"implement interface " + interfaceName);
}
}
//本地存根
if (stub != null) {
if ("true".equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not " +
"implement interface " + interfaceName);
}
}
//校验application,如果为空则从系统变量赋值
checkApplication();
//校验RegistryConfig,如果为空则从系统变量赋值
checkRegistry();
//校验protocols,如果为空则从系统变量赋值
checkProtocol();
//从系统变量为serviceconfig赋值
appendProperties(this);
//校验本地存根和本地伪装
checkStub(interfaceClass);
checkMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
//服务暴露
doExportUrls();
// ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息。
// 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel。
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), ref, interfaceClass);
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}

这个方法里主要做了这么几件事:

  1. 校验防止重复服务暴露
  2. 创建ProviderConfig,并赋值
  3. 泛型化校验
  4. 校验要暴露的接口
  5. 本地存根配置
  6. 校验ApplicationConfig、RegistryConfig、ProtocolConfig
  7. 服务暴露
  8. 暴露完后将暴露的服务存入providedServices中

    所以,总体来说就是做校验和config的初始化工作,然后调用doExportUrls进行服务暴露。

接下来我们看看doExportUrls方法:

private void doExportUrls() {
//将Registries封装成URL
List<URL> registryURLs = loadRegistries(true);
//遍历protocolConfig,并注册
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}

讲RegistryConfig转换为URL对象,然后调用doExportUrlsFor1Protocol方法。

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
....忽略不相关代码
String scope = url.getParameter(Constants.SCOPE_KEY);
// don't export when none is configured
if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
//本地暴露
// export to local if the config is not remote (export to remote only when config is remote)
if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
//远程暴露
// export to remote if the config is not local (export to local only when config is local)
if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && !registryURLs.isEmpty()) {
for (URL registryURL : registryURLs) {
//有时候希望人工管理服务提供者的上线和下线,此时需将注册中心标识为非动态管理模式。
//dynamic="false" 为人工管理服务模式
url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY,
registryURL.getParameter(Constants.DYNAMIC_KEY));
//监控中心
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
//配置是JdkProxyFactory还是JavassistProxyFactory
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(Constants.PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
}
//ProxyFactory$Adaptive
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//Protocol$Adaptive
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
}

doExportUrlsFor1Protocol代码里面有很多配置和设值的代码,我这里就不贴出来了,有兴趣的可以自己去看看是怎么设值的。

这段代码我们只需要看这个if判断就可以了。在这个if判断里面,如果scope配置的不是none,并且不是remote,那么就会进行本地暴露和远程暴露。

下面我只对远程暴露进行讲解。

首先会对url进行dynamic和monitorUrl校验和配置,然后会调用proxyFactory#getInvoker生成一个invoker对象。proxyFactory是由dubbo的spi生成的代理对象ProxyFactory$Adaptive,我们来看看具体的代码:

public class ProxyFactory$Adaptive implements ProxyFactory {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
private AtomicInteger count = new AtomicInteger(0); public Object getProxy(Invoker var1) throws RpcException {
if (var1 == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
} else if (var1.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
} else {
URL var2 = var1.getUrl();
String var3 = var2.getParameter("proxy", "javassist");
if (var3 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var2.toString() + ") use keys([proxy])");
} else {
ProxyFactory var4 = null; try {
var4 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var3);
} catch (Exception var6) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var3 + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", var6);
} var4 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
} return var4.getProxy(var1);
}
}
} public Object getProxy(Invoker var1, boolean var2) throws RpcException {
if (var1 == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
} else if (var1.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
} else {
URL var3 = var1.getUrl();
String var4 = var3.getParameter("proxy", "javassist");
if (var4 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var3.toString() + ") use keys([proxy])");
} else {
ProxyFactory var5 = null; try {
var5 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var4);
} catch (Exception var7) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var4 + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", var7);
} var5 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
} return var5.getProxy(var1, var2);
}
}
}
//var1 = ref
//var2 = (Class) interfaceClass
//var3 = url 对象
public Invoker getInvoker(Object var1, Class var2, URL var3) throws RpcException {
if (var3 == null) {
throw new IllegalArgumentException("url == null");
} else {
//获取代理方式,默认是javassist
String var5 = var3.getParameter("proxy", "javassist");
if (var5 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var3.toString() + ") use keys([proxy])");
} else {
ProxyFactory var6 = null; try {
//通过spi获取ProxyFactory实例JavassistProxyFactory
var6 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var5);
} catch (Exception var8) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var5 + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", var8);
} var6 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
}
//调用JavassistProxyFactory实例的invoker方法
return var6.getInvoker(var1, var2, var3);
}
}
} public ProxyFactory$Adaptive() {
}
}

所以proxyFactory#getInvoker最终会通过 ProxyFactory$Adaptive生成一个invoker对象。

然后封装成wrapperInvoker实例传入到protocol#export中。protocol是生成的代理类Protocol$Adaptive。

我们看一下Protocol$Adaptive生成的代码是怎么样的:

public class Protocol$Adaptive implements Protocol {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
private AtomicInteger count = new AtomicInteger(0); public void destroy() {
throw new UnsupportedOperationException("method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
} public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
} public Exporter export(Invoker var1) throws RpcException {
if (var1 == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
} else if (var1.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
} else {
URL var2 = var1.getUrl();
String var3 = var2.getProtocol() == null ? "dubbo" : var2.getProtocol();
if (var3 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + var2.toString() + ") use keys([protocol])");
} else {
Protocol var4 = null; try {
var4 = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(var3);
} catch (Exception var6) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var3 + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", var6);
} var4 = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
} return var4.export(var1);
}
}
} public Invoker refer(Class var1, URL var2) throws RpcException {
if (var2 == null) {
throw new IllegalArgumentException("url == null");
} else {
String var4 = var2.getProtocol() == null ? "dubbo" : var2.getProtocol();
if (var4 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + var2.toString() + ") use keys([protocol])");
} else {
Protocol var5 = null; try {
var5 = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(var4);
} catch (Exception var7) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var4 + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", var7);
} var5 = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
} return var5.refer(var1, var2);
}
}
} public Protocol$Adaptive() {
}
}

最后会通过下面代码,获得ProtocolListenerWrapper实例,并调用其export方法进行暴露。

var4 = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(var3);

然后调用链:ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol

最后调用RegistryProtocol#export方法。

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//服务端开启服务
//export invoker
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker); URL registryUrl = getRegistryUrl(originInvoker); //获取注册中心实例
//registry provider
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker); //to judge to delay publish whether or not
boolean register = registeredProviderUrl.getParameter("register", true); ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl); if (register) {
//注册
register(registryUrl, registeredProviderUrl);
ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
} // Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
//订阅监听
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
}

我们先看doLocalExport这个方法,这个方法里面实现了服务端的服务暴露。

private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
String key = getCacheKey(originInvoker);
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker,
getProviderUrl(originInvoker));
//通过spi调用dubboProtocol进行服务暴露
exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete),
originInvoker);
//缓存暴露过的服务
bounds.put(key, exporter);
}
}
}
return exporter;
}

在doLocalExport方法里面,会通过spi调用

ProtocolListenerWrapper->ProtocolFilterWrapper->DubboProtocol

DubboProtocol#export

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl(); // export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter); //export an stub service for dispatching event
Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
if (isStubSupportEvent && !isCallbackservice) {
String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
if (logger.isWarnEnabled()) {
logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
"], has set stubproxy support event ,but no stub methods founded."));
}
} else {
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
//实现服务的暴露
openServer(url);
optimizeSerialization(url);
return exporter;
} private void openServer(URL url) {
...忽略代码
if (server == null) {
//创建服务
serverMap.put(key, createServer(url));
}
} private ExchangeServer createServer(URL url) {
...忽略代码
//启动服务
server = Exchangers.bind(url, requestHandler);
...忽略代码
return server;
}

最终会通过Exchangers#bind来启动服务

Exchangers#bind->HeaderExchanger#bind->Transporters#bind->NettyTransporter#bind->NettyServer#doOpen

最后会在NettyServer调用doOpen方法启动服务。

我们再回到RegistryProtocol#export往下走

register(registryUrl, registeredProviderUrl),这句代码就是用来注册到注册中心的,如果用的是zk,那么就是注册到Zookeeper的。

我们进到这个方法里瞧瞧。

public void register(URL registryUrl, URL registedProviderUrl) {
//ZookeeperRegistry
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registedProviderUrl);
}

registryFactory是spi实现的代理类:

public class RegistryFactory$Adaptive implements RegistryFactory {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
private AtomicInteger count = new AtomicInteger(0); public Registry getRegistry(URL var1) {
if (var1 == null) {
throw new IllegalArgumentException("url == null");
} else {
String var3 = var1.getProtocol() == null ? "dubbo" : var1.getProtocol();
if (var3 == null) {
throw new IllegalStateException("Fail to get extension(org.apache.dubbo.registry.RegistryFactory) name from url(" + var1.toString() + ") use keys([protocol])");
} else {
RegistryFactory var4 = null; try {
var4 = (RegistryFactory)ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(var3);
} catch (Exception var6) {
if (this.count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + var3 + " for type org.apache.dubbo.registry.RegistryFactory, will use default extension dubbo instead.", var6);
} var4 = (RegistryFactory)ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension("dubbo");
} return var4.getRegistry(var1);
}
}
} public RegistryFactory$Adaptive() {
}
}

然后通过调用RegistryFactory$Adaptive的getRegistry方法获取Registry的实现类ZookeeperRegistry实例。

ZookeeperRegistry继承关系图如下所示:

所以调用ZookeeperRegistry#register会先调用到父类的register方法。

FailbackRegistry#register

public void register(URL url) {
super.register(url);
//从失败集合中移除
failedRegistered.remove(url);
//从失败取消注册的集合中移除
failedUnregistered.remove(url);
try {
// Sending a registration request to the server side
//调用子类的方法进行注册
doRegister(url);
} catch (Exception e) {
Throwable t = e; // If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
} // Record a failed registration request to a failed list, retry regularly
failedRegistered.add(url);
}
}

通过调用doRegister回到ZookeeperRegistry的doRegister方法中,然后调用zk的spi进行注册。

到这里,服务端的暴露也就讲完了。

源码分析--dubbo服务端暴露的更多相关文章

  1. 4. 源码分析---SOFARPC服务端暴露

    服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...

  2. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  3. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  4. 源码分析Dubbo服务消费端启动流程

    通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...

  5. Nacos(二)源码分析Nacos服务端注册示例流程

    上回我们讲解了客户端配置好nacos后,是如何进行注册到服务器的,那我们今天来讲解一下服务器端接收到注册实例请求后会做怎么样的处理. 首先还是把博主画的源码分析图例发一下,让大家对整个流程有一个大概的 ...

  6. dubbo源码分析13——服务本地暴露 exportLocal(url)

    dubbo服务的本地暴露,显然是针对当服务消费者和服务提供者都在同一个jvm的进程内这种场景 .通常是发生在服务之间的调用的情况下.一种情况就是A服务调用B服务的情况,如果A服务和B服务都是在一个线程 ...

  7. Netty源码分析之服务端启动过程

    一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...

  8. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  9. TeamTalk源码分析之服务端描述

    TTServer(TeamTalk服务器端)主要包含了以下几种服务器: LoginServer (C++): 登录服务器,分配一个负载小的MsgServer给客户端使用 MsgServer (C++) ...

随机推荐

  1. Day1 -Python program

    采用python 3.5 用PyCharm编译 第一串代码 print ("hello,world!") 练习1 输入一个用户名和密码,如果输入正确,就欢迎登陆,否则就显示错误. ...

  2. SP1805 HISTOGRA - Largest Rectangle in a Histogram 题解

    题目链接:https://www.luogu.org/problemnew/show/SP1805 分析: 我们可以用一个单调栈由低到高来存储它的高度,并用数组对每个高度记录一下它前面(包括它自己)一 ...

  3. 洛谷P1033 自由落体 题解

    题目链接:https://www.luogu.org/problemnew/show/P1033 呵呵,真的学好物理比较重要,前些年卡在这题上的我今天终于会做了,可恶的自由落体(也许是我太弱了吧 ) ...

  4. [HAOI2006]聪明的猴子 题解

    题意: 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上.猴子不会游泳,但跳跃能力比较强,它们仍然可以在 ...

  5. 后端 - Lession 01 PHP 基础

    目录 Lession 01 php 基础 1. php 基础 2. php 变量 3. php 单引号 和 双引号区别 4. 数据类型 5. 数据类型转换 6. 常量 7. 运算符 8. 为 fals ...

  6. 个人永久性免费-Excel催化剂功能第93波-地图数据挖宝之两点距离的路径规划

    在日常手机端,网页端的向地图发出两点距离的行程规划,相信绝大多数人都有用到过,但毕竟是个体单一行为,若某些时候需要用到批量性的操作,就显得很不现实了,同时,数据只是在应用或网页内,非结构化的数据,也是 ...

  7. 个人永久性免费-Excel催化剂功能第85波-灵活便捷的批量发送短信功能(使用腾讯云接口)

    微信时代的今天,短信一样不可缺席,大系统都有集成短信接口.若只是临时用一下,若能够直接在Excel上加工好内容就可以直接发送,这些假设在此篇批量群发短信功能中都为大家带来完美答案. 业务场景 不多说, ...

  8. RecycleView文字吸顶,点击吸顶布局刷新数据

    实现效果 需求 Recycle有一个头布局,可以跟随列表进行滑动 点击头布局之后可以重新加载列表数据 随着头布局的消失,留下一个可点击的布局(该布局在头布局中) 效果类似下图: 淘宝的商品列表,随着我 ...

  9. spark 源码分析之十九 -- Stage的提交

    引言 上篇 spark 源码分析之十九 -- DAG的生成和Stage的划分 中,主要介绍了下图中的前两个阶段DAG的构建和Stage的划分. 本篇文章主要剖析,Stage是如何提交的. rdd的依赖 ...

  10. context创建过程解析(二)之deployWARs

    HostConfig.deployApps() //在监听到start事件类型,也就是StandardHost调用startInternal protected void deployApps() { ...