dubbo启动流程之服务暴露
1.DubboBootstrap类启动
1.1 入口:start()方法
start方法中包含了服务暴露和服务引用,这里跟踪服务的暴露流程
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
startup.set(false);
initialized.set(false);
shutdown.set(false);
awaited.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. 暴露 dubbo服务(provider端)
exportServices();
if (isRegisterConsumerInstance() || hasExportedServices()) {
// 2. export MetadataService
exportMetadataService();
// 3. Register the local ServiceInstance if required
registerServiceInstance();
}
// 引用dubbo服务(consumer端)
referServices();
if (asyncExportingFutures.size() > 0 || asyncReferringFutures.size() > 0) {
new Thread(() -> {
try {
this.awaitFinish();
} catch (Exception e) {
logger.warn(NAME + " asynchronous export / refer occurred an exception.");
}
startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
onStart();
}).start();
} else {
startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
onStart();
}
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
1.2 暴露dubbo服务,exportServices()方法
会调用到ServiceConfig的export方法
private void exportServices() {
for (ServiceConfigBase sc : configManager.getServices()) {
ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
serviceConfig.setBootstrap(this);
if (!serviceConfig.isRefreshed()) {
serviceConfig.refresh();
}
// 异步起动
if (sc.shouldExportAsync()) {
ExecutorService executor = executorRepository.getExportReferExecutor();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
sc.export();
exportedServices.add(sc);
} catch (Throwable t) {
logger.error("export async catch error : " + t.getMessage(), t);
}
}, executor);
asyncExportingFutures.add(future);
}
// 非异步暴露
else {
// 调用暴露
sc.export();
exportedServices.add(sc);
}
}
}
1.3 调用ServiceConfig的export()方法
public synchronized void export() {
if (this.shouldExport() && !this.exported) {
this.init();
// check bootstrap state
if (!bootstrap.isInitialized()) {
throw new IllegalStateException("DubboBootstrap is not initialized");
}
if (!this.isRefreshed()) {
this.refresh();
}
if (!shouldExport()) {
return;
}
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 进一步调用doExport
doExport();
}
if (this.bootstrap.getTakeoverMode() == BootstrapTakeoverMode.AUTO) {
this.bootstrap.start();
}
}
}
1.4 调用doExport()
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
if (exported) {
return;
}
exported = true;
if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
// 调用doExportUrls暴露urls
doExportUrls();
exported();
}
1.5 调用doExportUrls()
如果有多个URL,则循环依次暴露出去
private void doExportUrls() {
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
// 如果有多个url,循环遍历
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);
// 暴露单个
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
1.6 暴露单个url, doExportUrlsFor1Protocol()
这里会创建动态代理,javassist或者jdk动态代理,如果使用javassist动态代理,创建 AbstractProxyInvoker(Wrapper),调用流程:invoker的doInvoke方法 -> wrapper.invokeMethod -> 用户的ServiceImpl方法,使用Wrapper, 会组装出Class, 然后使用组装出的类调用,从而避免了反射调用,比jdk动态代理的反射调用稍微好些。
包装器模式:new DelegateProviderMetaDataInvoker(new AbstractProxyInvoker())
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
.... 此处省略代码
// 非local, 暴露远程暴露
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
url = url.putAttribute(MONITOR_KEY, monitorUrl);
}
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url.getServiceKey() + " to registry " + registryURL.getAddress());
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url.getServiceKey());
}
}
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
// 这里创建动态代理,JavassistProxy或者JdkProxy
// 如果使用javassist动态代理,创建 AbstractProxyInvoker(Wrapper),
// 调用doInvoke -> wrapper.invokeMethod -> 用户的ServiceImpl方法
// 使用Wrapper, 会组装出Class, 然后使用组装出的类调用,从而避免了反射调用,比jdk动态代理的反射调用稍微好些
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.putAttribute(EXPORT_KEY, url));
// 包装了下,将(invoker + ServiceConfig)包装进去
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用protocol的export, 会调用到RegistryProtocol类的export
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (MetadataService.class.getName().equals(url.getServiceInterface())) {
MetadataUtils.saveMetadataURL(url);
}
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
MetadataUtils.publishServiceDefinition(url);
}
}
this.urls.add(url);
}
2. PROTOCOL暴露export()
2.1 RegistryProtocol.export()方法暴露dubbo服务
来到RegistryProtocol下也是经过dubbo spi aop包装的,流程:QosProtocolWrapper.export -> ProtocolFilterWrapper.export -> ProtocolListenerWrapper.export -> RegistryProtocol.export,但实际上QosProtocolWrapper、ProtocolFilterWrapper、ProtocolListenerWrapper根本没有做什么,这里强耦合了,属于多余的包装操作
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
URL providerUrl = getProviderUrl(originInvoker);
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// 暴露一个服务
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
register(registry, registeredProviderUrl);
}
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
notifyExport(exporter);
return new DestroyableExporter<>(exporter);
}
2.2 doLocalExport方法
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
// 将providerUrl包装进去
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// protocol.export调用链
// QosProtocolWrapper.export -> ProtocolFilterWrapper.export -> ProtocolListenerWrapper.export -> DubboProtocol.export
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
2.3 来到DubboProtocol的export方法
来到DubboProtocol的export,中间经过流程:QosProtocolWrapper.export -> ProtocolFilterWrapper.export -> ProtocolListenerWrapper.export -> DubboProtocol.export
其中ProtocolFilterWrapper会给invoker包装过滤器链
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
String key = serviceKey(url);
// 将invoker封装进DubboExporter,实际上exporter没啥大的作用,exporter有销毁invoker功能,很鸡肋
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// exporter 存入exporterMap
exporterMap.put(key, exporter);
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."));
}
}
}
// 打开服务,暴露端口(20880)出去,即开启NettyServer,启动Nettty服务端
openServer(url);
optimizeSerialization(url);
return exporter;
}
2.4 openServer开启服务
// 如果要暴露多个URL,只会创建一次NettyServer,因为开启netty服务端只需要暴露一个端口(20880)就行了
private void openServer(URL url) {
// 获取暴露地址. 例如: 192.168.56.1:20880
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) {
// 根据key看是否有server,有则表明有其他接口暴露过了,
// 不再暴露(暴露的NettyServer是共享端口192.168.56.1:20880)
ProtocolServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
if (server == null) {
// createServer 创建netty服务
serverMap.put(key, createServer(url));
}
}
} else {
server.reset(url);
}
}
}
2.5 createServer创建服务
传入requestHandler,将其绑定到NettyServer
private ProtocolServer createServer(URL url) {
url = URLBuilder.from(url)
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
.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 {
// 开始bind url和requestHandler,
// 这个requestHandler很重要,dubbo服务处理重要Handler, 通过此handler的reply方法
// 获取invoker,来到动态代理,最终调用到用户ServiceImpl中去
server = Exchangers.bind(url, requestHandler);
} 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);
}
2.6 Exchangers.bind
获取HeaderExchanger,再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");
// getExchanger获取到的是HeaderExchanger
return getExchanger(url).bind(url, handler);
}
2.7 HeaderExchanger.bind
包装handler, 组装调用链的过程,DecodeHandler用来做数据反序列化,默认hession2
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// 这里又是使用包装器模式,层层包装handler,其中重要的是
// new NettyServer(new DecodeHandler(new HeaderExchangeHandler(handler))
// DecodeHandler是用反序列化的,如hession(在业务线程中处理)
// 区别于dubbo协议的拆包粘包数据格式处理decode、incode(这个是在io线程中处理的)
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
2.8 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);
}
// getTransporter获取的是 org.apache.dubbo.remoting.transport.netty4.NettyTransporter
return getTransporter().bind(url, handler);
}
3 开始创建NettyServer
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
return new NettyServer(url, handler);
}
3.1 调用NettyServer的构造方法
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// ChannelHandlers.wrap(handler, url) 包装handler
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
3.1.1 包装hander过程
形成了hander包装链,NettyServerHandler -> NettyServer -> MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler -> DecodeHandler -> HeaderExangeHandler -> DubboProtocol类下的ExchangeHandlerAdapter。其中到AllChannelHandler时,会跳转到业务线程处理:executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); 调用run方法,再调用handler.received(channel, message),继续后续流程,最后来到了DubboProtocol类下的ExchangeHandlerAdapter.reply方法下。
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
// 通过dubbo spi获取到AllChannelHandler
// 最终结果:NettyServerHandler(NettyServer(MultiMessageHandler(HeartbeatHandler(AllChannelHandler(DecodeHandler(HeaderExangeHandler(DubboProtocol类下的ExchangeHandlerAdapter))))) ))
// 此包装链即为dubbo服务handler 从NettyServerHandler开始到ExchangeHandlerAdapter的reply方法的调用链
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
3.1.2 分析DubboProtocol类下的ExchangeHandlerAdapter.reply方法
getInvoker,获取invoker,其实就是从exporterMap获取exporter,再从exporter中获取invoker, 这个invoker也是包装器模式层层包装过的invoker,主要是:各种FilterInvoker(FilterChainNode过滤器链) -> DelegateProviderMetaDataInvoker ->JavassistProxyFactory下的AbstractProxyInvoker -> 用户业务代码
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
if (!(message instanceof Invocation)) {
throw new RemotingException(channel, "Unsupported request: "
+ (message == null ? null : (message.getClass().getName() + ": " + message))
+ ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
// need to consider backward-compatibility if it's a callback
if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
String methodsStr = invoker.getUrl().getParameters().get("methods");
boolean hasMethod = false;
if (methodsStr == null || !methodsStr.contains(",")) {
hasMethod = inv.getMethodName().equals(methodsStr);
} else {
String[] methods = methodsStr.split(",");
for (String method : methods) {
if (inv.getMethodName().equals(method)) {
hasMethod = true;
break;
}
}
}
if (!hasMethod) {
logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()
+ " not found in callback service interface ,invoke will be ignored."
+ " please update the api interface. url is:"
+ invoker.getUrl()) + " ,invocation is :" + inv);
return null;
}
}
RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
Result result = invoker.invoke(inv);
return result.thenApply(Function.identity());
}
3.2 NettyServer构造器中会调用doOpen()方法,开启Netty服务
这里就是使用Netty api操作,开启服务暴露端口,主要的是在pipeline中加入了3个重要的bound处理器
- dubbo协议编码器,InternalEncoder (OutboundHandler)
- dubbo协议解码器,InternalDecoder (InboundHandler)
- dubbo服务调用流程处理器,NettyServerHandler (InboundHandler和OutboundHandler)
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
workerGroup = NettyEventLoopFactory.eventLoopGroup(
getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
"NettyServerWorker");
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE);
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.SO_KEEPALIVE, keepalive)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
// dubbo协议解码器,InternalDecoder
.addLast("decoder", adapter.getDecoder())
// dubbo协议编码器,InternalEncoder
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
// dubbo服务调用流程处理器,NettyServerHandler
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}