dubbo提供方服务启动方式
dubbo使用版本为2.7.6。
一、介绍
本文主要基于dubbo提供的demo运行过程介绍一些dubbo服务方的启动过程,后面会结合服务消费方对整个调用过程进行一次整合。
二、服务介绍

以官方提供的demo为例子,我们选择dubbo-demo-api,其中有两个module,分别是提供方和消费方,这两个demo采用的是api的方式注册和调用服务的,而不是大家比较熟悉的xml方式。
如下是服务提供方的服务:
public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } @Override public CompletableFuture<String> sayHelloAsync(String name) { return null; } }
三、服务提供方启动过程
1.启动类
public class Application { public static void main(String[] args) throws Exception { if (isClassic(args)) { startWithExport(); } else { startWithBootstrap(); } } private static boolean isClassic(String[] args) { return args.length > 0 && "classic".equalsIgnoreCase(args[0]); }
//调用该方法 private static void startWithBootstrap() { ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))//应用名称 .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))//zk注册地址 .service(service)//制定要注册的服务 .start()//提供方启动 .await(); } private static void startWithExport() throws InterruptedException { ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setApplication(new ApplicationConfig("dubbo-demo-api-provider")); service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); service.export(); System.out.println("dubbo service started"); new CountDownLatch(1).await(); } }
如上所示,在设定了相关必要信息之后,调用了DubboBootstrap.start()方法开始启动服务提供方了:
public DubboBootstrap start() { if (started.compareAndSet(false, true)) { ready.set(false); initialize();//主要初始化配置中心和获取远程配置 if (logger.isInfoEnabled()) { logger.info(NAME + " is starting..."); } // 1. export Dubbo Services exportServices();//服务暴露,并启动开启服务 // Not only provider register if (!isOnlyRegisterProvider() || hasExportedServices()) { // 2. export MetadataService exportMetadataService(); //3. Register the local ServiceInstance if required registerServiceInstance(); } referServices();//服务引用,这里消费方那里会重点介绍 if (asyncExportingFutures.size() > 0) { new Thread(() -> { try { this.awaitFinish(); } catch (Exception e) { logger.warn(NAME + " exportAsync occurred an exception."); } ready.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } }).start(); } else { ready.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } } if (logger.isInfoEnabled()) { logger.info(NAME + " has started."); } } return this; }
我们来到exportServices()方法:
private void exportServices() { configManager.getServices().forEach(sc -> { // TODO, compatible with ServiceConfig.export() ServiceConfig serviceConfig = (ServiceConfig) sc; serviceConfig.setBootstrap(this); if (exportAsync) { ExecutorService executor = executorRepository.getServiceExporterExecutor(); Future<?> future = executor.submit(() -> { sc.export(); exportedServices.add(sc); }); asyncExportingFutures.add(future); } else { sc.export();//没有配置exportAsync,这里直接调用ServiceConfig的export()方法 exportedServices.add(sc); } }); }
查看ServiceConfig的export()方法:
public synchronized void export() { if (!shouldExport()) { return; } if (bootstrap == null) { bootstrap = DubboBootstrap.getInstance(); bootstrap.init(); } checkAndUpdateSubConfigs(); //init serviceMetadata serviceMetadata.setVersion(version); serviceMetadata.setGroup(group); serviceMetadata.setDefaultGroup(group); serviceMetadata.setServiceType(getInterfaceClass()); serviceMetadata.setServiceInterfaceName(getInterface()); serviceMetadata.setTarget(getRef()); if (shouldDelay()) { DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS); } else { doExport(); } exported(); }
上面封装了serviceMetadata(元数据),可以后面注册到元数据中心,这里不细说了,接下来调用的是doExport()-->doExportUrls():
private void doExportUrls() { ServiceRepository repository = ApplicationModel.getServiceRepository(); ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
//服务信息存储到内存中 repository.registerProvider( getUniqueServiceName(), ref, serviceDescriptor, this, serviceMetadata );
//封装要注册服务的URL List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
//对每一个支持的协议进行服务暴露 for (ProtocolConfig protocolConfig : protocols) { String pathKey = URL.buildKey(getContextPath(protocolConfig) .map(p -> p + "/" + path) .orElse(path), group, version); // In case user specified path, register service one more time to map it to path. repository.registerService(pathKey, interfaceClass); // TODO, uncomment this line once service key is unified serviceMetadata.setServiceKey(pathKey); doExportUrlsFor1Protocol(protocolConfig, registryURLs);//实际的服务暴露 } }
来到ServiceConfig.doExportUrlsFor1Protocol():
1 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { 2 String name = protocolConfig.getName(); 3 if (StringUtils.isEmpty(name)) { 4 name = DUBBO; 5 } 6 7 Map<String, String> map = new HashMap<String, String>(); 8 map.put(SIDE_KEY, PROVIDER_SIDE); 9 10 ServiceConfig.appendRuntimeParameters(map); 11 AbstractConfig.appendParameters(map, getMetrics()); 12 AbstractConfig.appendParameters(map, getApplication()); 13 AbstractConfig.appendParameters(map, getModule()); 14 // remove 'default.' prefix for configs from ProviderConfig 15 // appendParameters(map, provider, Constants.DEFAULT_KEY); 16 AbstractConfig.appendParameters(map, provider); 17 AbstractConfig.appendParameters(map, protocolConfig); 18 AbstractConfig.appendParameters(map, this); 19 MetadataReportConfig metadataReportConfig = getMetadataReportConfig(); 20 if (metadataReportConfig != null && metadataReportConfig.isValid()) { 21 map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE); 22 } 23 if (CollectionUtils.isNotEmpty(getMethods())) { 24 for (MethodConfig method : getMethods()) { 25 AbstractConfig.appendParameters(map, method, method.getName()); 26 String retryKey = method.getName() + ".retry"; 27 if (map.containsKey(retryKey)) { 28 String retryValue = map.remove(retryKey); 29 if ("false".equals(retryValue)) { 30 map.put(method.getName() + ".retries", "0"); 31 } 32 } 33 List<ArgumentConfig> arguments = method.getArguments(); 34 if (CollectionUtils.isNotEmpty(arguments)) { 35 for (ArgumentConfig argument : arguments) { 36 // convert argument type 37 if (argument.getType() != null && argument.getType().length() > 0) { 38 Method[] methods = interfaceClass.getMethods(); 39 // visit all methods 40 if (methods.length > 0) { 41 for (int i = 0; i < methods.length; i++) { 42 String methodName = methods[i].getName(); 43 // target the method, and get its signature 44 if (methodName.equals(method.getName())) { 45 Class<?>[] argtypes = methods[i].getParameterTypes(); 46 // one callback in the method 47 if (argument.getIndex() != -1) { 48 if (argtypes[argument.getIndex()].getName().equals(argument.getType())) { 49 AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex()); 50 } else { 51 throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType()); 52 } 53 } else { 54 // multiple callbacks in the method 55 for (int j = 0; j < argtypes.length; j++) { 56 Class<?> argclazz = argtypes[j]; 57 if (argclazz.getName().equals(argument.getType())) { 58 AbstractConfig.appendParameters(map, argument, method.getName() + "." + j); 59 if (argument.getIndex() != -1 && argument.getIndex() != j) { 60 throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType()); 61 } 62 } 63 } 64 } 65 } 66 } 67 } 68 } else if (argument.getIndex() != -1) { 69 AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex()); 70 } else { 71 throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>"); 72 } 73 74 } 75 } 76 } // end of methods for 77 } 78 79 if (ProtocolUtils.isGeneric(generic)) { 80 map.put(GENERIC_KEY, generic); 81 map.put(METHODS_KEY, ANY_VALUE); 82 } else { 83 String revision = Version.getVersion(interfaceClass, version); 84 if (revision != null && revision.length() > 0) { 85 map.put(REVISION_KEY, revision); 86 } 87 88 String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); 89 if (methods.length == 0) { 90 logger.warn("No method found in service interface " + interfaceClass.getName()); 91 map.put(METHODS_KEY, ANY_VALUE); 92 } else { 93 map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); 94 } 95 } 96 97 /** 98 * Here the token value configured by the provider is used to assign the value to ServiceConfig#token 99 */ 100 if(ConfigUtils.isEmpty(token) && provider != null) { 101 token = provider.getToken(); 102 } 103 104 if (!ConfigUtils.isEmpty(token)) { 105 if (ConfigUtils.isDefault(token)) { 106 map.put(TOKEN_KEY, UUID.randomUUID().toString()); 107 } else { 108 map.put(TOKEN_KEY, token); 109 } 110 } 111 //init serviceMetadata attachments 112 serviceMetadata.getAttachments().putAll(map); 113 114 // export service 115 String host = findConfigedHosts(protocolConfig, registryURLs, map); 116 Integer port = findConfigedPorts(protocolConfig, name, map); 117 URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map); 118 119 // You can customize Configurator to append extra parameters 120 if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) 121 .hasExtension(url.getProtocol())) { 122 url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) 123 .getExtension(url.getProtocol()).getConfigurator(url).configure(url); 124 } 125 126 String scope = url.getParameter(SCOPE_KEY);//这里获取URL中的scope参数,来指定用何种暴露方式,如果没有配置,则本地和远程都暴露 127 // don't export when none is configured 128 if (!SCOPE_NONE.equalsIgnoreCase(scope)) { 129 130 // export to local if the config is not remote (export to remote only when config is remote) 131 if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) { 132 exportLocal(url);//服务本地暴露 133 } 134 // export to remote if the config is not local (export to local only when config is local) 135 if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) { 136 if (CollectionUtils.isNotEmpty(registryURLs)) { 137 for (URL registryURL : registryURLs) { 138 //if protocol is only injvm ,not register 139 if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { 140 continue; 141 } 142 url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY)); 143 URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL); 144 if (monitorUrl != null) { 145 url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString()); 146 } 147 if (logger.isInfoEnabled()) { 148 if (url.getParameter(REGISTER_KEY, true)) { 149 logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); 150 } else { 151 logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); 152 } 153 } 154 155 // For providers, this is used to enable custom proxy to generate invoker 156 String proxy = url.getParameter(PROXY_KEY); 157 if (StringUtils.isNotEmpty(proxy)) { 158 registryURL = registryURL.addParameter(PROXY_KEY, proxy); 159 } 160 //获取所支持协议对应的Invoker 161 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); 162 Exporter<?> exporter = PROTOCOL.export(invoker);//调用Protocol实现的export() 163 exporters.add(exporter); 164 } 165 } else { 166 if (logger.isInfoEnabled()) { 167 logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); 168 } 169 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url); 170 Exporter<?> exporter = PROTOCOL.export(invoker); 171 exporters.add(exporter); 172 } 173 /** 174 * @since 2.7.0 175 * ServiceData Store 176 */ 177 WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE)); 178 if (metadataService != null) { 179 metadataService.publishServiceDefinition(url); 180 } 181 } 182 } 183 this.urls.add(url); 184 }
注:上面第161行调用了ProxyFactory$Adaptive.java(运行过程中由dubbo自动生产的类)的getInvoker()方法来获取接口对应的代理Invoker(默认用的是javassist代理):
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory { ...
public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException { if (arg2 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg2; String extName = url.getParameter("proxy", "javassist");//默认用的是javassist代理 if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])"); org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName); return extension.getInvoker(arg0, arg1, arg2); }
...
}
再回到ServiceConfig.doExportUrlsFor1Protocol(),第162行中调用了实现Proctol接口协议的export()方法:

上面方法中获取了URL中的protocol属性值,这里值为registry,对应的Protocol实现为RegistryProctol,接下来沿着调用链路会调到RegistryProctol.export():
1 @Override 2 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { 3 URL registryUrl = getRegistryUrl(originInvoker); 4 // url to export locally 5 URL providerUrl = getProviderUrl(originInvoker); 6 7 // Subscribe the override data 8 // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call 9 // the same service. Because the subscribed is cached key with the name of the service, it causes the 10 // subscription information to cover. 11 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl); 12 final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); 13 overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); 14 15 providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); 16 providerUrl = UrlUtils.unmodifiableUrl(providerUrl); 17 18 //export invoker 19 final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl); 20 21 // url to registry 22 final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl); 23 24 // decide if we need to delay publish 25 boolean register = providerUrl.getParameter(REGISTER_KEY, true); 26 if (register) { 27 //服务注册 28 register(registryUrl, registeredProviderUrl); 29 } 30 31 // register stated url on provider model 32 registerStatedUrl(registryUrl, registeredProviderUrl, register); 33 34 // Deprecated! Subscribe to override rules in 2.6.x or before. 35 final Registry registry = registryFactory.getRegistry(registryUrl); 36 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); 37 38 exporter.setRegisterUrl(registeredProviderUrl); 39 exporter.setSubscribeUrl(overrideSubscribeUrl); 40 41 notifyExport(exporter); 42 //Ensure that a new exporter instance is returned every time export 43 return new DestroyableExporter<>(exporter); 44 }
上面第19行调用了doLocalExport()方法,沿着调用链路,最终会通过Protocol$Adaptive获取Protocol的默认实现DubboProtocol。

Proctol默认实现是dubbo协议,Proctol接口默认实现是DubboProctol(通过Protocol$Adaptive.export()获取),接下来看看DubboProctol.export()方法:
@Override 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(STUB_EVENT_KEY, DEFAULT_STUB_EVENT); Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false); if (isStubSupportEvent && !isCallbackservice) { String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY); if (stubServiceMethods == null || stubServiceMethods.length() == 0) { if (logger.isWarnEnabled()) { logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) + "], has set stubproxy support event ,but no stub methods founded.")); } } } openServer(url);//服务提供方Server开启 optimizeSerialization(url); return exporter; } private void openServer(URL url) { // find server. String key = url.getAddress(); //client can export a service which's only for server to invoke boolean isServer = url.getParameter(IS_SERVER_KEY, true); if (isServer) { ProtocolServer server = serverMap.get(key); if (server == null) { synchronized (this) { server = serverMap.get(key); if (server == null) { serverMap.put(key, createServer(url));//调用createServer()创建并启动Server } } } else { // server supports reset, use together with override server.reset(url); } } } private ProtocolServer createServer(URL url) { url = URLBuilder.from(url) // send readonly event when server closes, it's enabled by default .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString()) // enable heartbeat by default .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT)) .addParameter(CODEC_KEY, DubboCodec.NAME) .build(); String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER); if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) { throw new RpcException("Unsupported server type: " + str + ", url: " + url); } ExchangeServer server; try { server = Exchangers.bind(url, requestHandler);//创建并启动Server } catch (RemotingException e) { throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e); } str = url.getParameter(CLIENT_KEY); if (str != null && str.length() > 0) { Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(); if (!supportedTypes.contains(str)) { throw new RpcException("Unsupported client type: " + str); } } return new DubboProtocolServer(server); }
看看Exchangers.bind()方法:
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange"); return getExchanger(url).bind(url, handler); }
接下来调用HeaderExchanger.bind():
@Override public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); }
再往下,来到Transporters.bind():
public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handlers == null || handlers.length == 0) { throw new IllegalArgumentException("handlers == null"); } ChannelHandler handler; if (handlers.length == 1) { handler = handlers[0]; } else { handler = new ChannelHandlerDispatcher(handlers); } return getTransporter().bind(url, handler); }
public static Transporter getTransporter() {
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
上面方法中调用了getTransporter()获取Transporter接口的实现,从@SPI("netty")可见,默认实现是Netty。

因此接下来调用Transporter接口的实现NettyTransporter的bind()方法:
/** * Default extension of {@link Transporter} using netty4.x. */ public class NettyTransporter implements Transporter { public static final String NAME = "netty"; @Override public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException { return new NettyServer(url, handler); } @Override public Client connect(URL url, ChannelHandler handler) throws RemotingException { return new NettyClient(url, handler); } }
如上所示,在bind()方法中new了一个NettyServer(),级创建并开启了一个NettyServer用于接收服务消费方(NettyClient)对提供方(NettyServer)的服务调用。关于NettyServer启动过程请看我之前的文章。

浙公网安备 33010602011771号