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启动过程请看我之前的文章。