provider服务暴露过程分为两种:
1.发布本地服务【Injvm】,主要供本地环境服务间调用
2.发布远程服务【protocol】,根据注册中心地址列表将服务依次发布到注册中心上。
整体交互图如下:
下面开始讲解本地暴露和远程暴露过程:
1.本地服务暴露(Injvm)
1.1 本地服务 Invoker构建
ServiceConfig.java:
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();//InjvmProtocol private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); // JavassistProxyFactory private void exportLocal(URL url) { if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0); Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } }
从源码可知,本地服务暴露协议头为 Injvm,设置好protocol后直接调用proxyFactory.getInvoker 获取本地服务Invoker,最后通知InjvmProtocol.export发布服务
下面看看
JavassistProxyFactory getInvoker如何工作的:
JavassistProxyFactory.java :
public class JavassistProxyFactory extends AbstractProxyFactory { @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO Wrapper类不能正确处理带$的类名 //根据class获取Wrapper,接着通过wrapper调用 final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type); return new AbstractProxyInvoker<T>(proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable { return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); } }; } }
Wrapper类是专门生成实例类的 Wrapper包装的工具类,分析getWrapper后得知,最后Wrapper给 com.alibaba.dubbo.demo.provider.DemoServiceImpl生成的wrapper类源代码如下:
com.alibaba.dubbo.common.bytecode.Wrapper1 extends Wrapper implements com.alibaba.dubbo.common.bytecode.ClassGenerator.DC { public static String[] pns; // property name array. public static java.util.Map pts; // property type map. public static String[] mns; // all method name array. 这里等于 [sayHello] public static String[] dmns; // declared method name array. 这里等于 [sayHello] public static Class[] mts0;// 这里是[class java.lang.String] public Wrapper1(){} public String[] getPropertyNames(){ return pns; } public boolean hasProperty(String n){ return pts.containsKey($1); } public Class getPropertyType(String n){ return (Class)pts.get($1); } public String[] getMethodNames(){ return mns; } public String[] getDeclaredMethodNames(){ return dmns; } public void setPropertyValue(Object o, String n, Object v){ com.alibaba.dubbo.demo.provider.DemoServiceImpl w; try{ w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl)$1); // $1 即为入参1 }catch(Throwable e){ throw new IllegalArgumentException(e); } throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+$2+"\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl."); } public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{ com.alibaba.dubbo.demo.provider.DemoServiceImpl w; try{ w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl)$1); // $1 即为入参1 }catch(Throwable e){ throw new IllegalArgumentException(e); } try{ if( "sayHello".equals( $2 ) && $3.length == 1 ) { // $2 即为入参2 $3即为入参3 class[] p return ($w)w.sayHello((java.lang.String)$4[0]); } } catch(Throwable e) { throw new java.lang.reflect.InvocationTargetException(e); } throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl."); } public Object getPropertyValue(Object o, String n){ com.alibaba.dubbo.demo.provider.DemoServiceImpl w; try{ w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl)$1); }catch(Throwable e){ throw new IllegalArgumentException(e); } throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+$2+"\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl."); } }
最终
JavassistProxyFactory 返回了一个抽象
匿名类
AbstractProxyInvoker
1.2 发布本地服务 export()
Invoker构造完成后开始执行export对invoker进行服务发布:
protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();//InjvmProtocol Exporter<?> exporter = protocol.export(Invoker invoker)
export的Protocol会根据url的protocol=injvm获取具体的SPI,最终会获取到 InjvmProtocol来处理export;
又因为
Protocol
的SPI包含 Wrapper类【 ProtocolListenerWrapper, 管理Filter链的 ProtocolFilterWrapper】,因此protocol.export()类的调用过程为:
ProtocolListenerWrapper
.export(invoker)
->
ProtocolFilterWrapper
.export(invoker) //这一步会将所有Provider端的FIlter链加载并构建出基于Filter链调用的Invoker【
最后构建成 EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter -> TraceFilter -> MonitorFilter -> TimeoutFilter -> ExceptionFilter -> 具体的Invoker 链】如下图:
-> InjvmProtocol.export(invoker) //
这里的invoker是包含了Filter链的Invoker
InjvmProtocol 最终根据传入的 Invoker,待发布的服务名称【如 com.alibaba.dubbo.demo.DemoService】构建实例 InjvmExporter:
InjvmProtocol.export():
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap); }
InjvmExporter
.java
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) { super(invoker); this.key = key; this.exporterMap = exporterMap; exporterMap.put(key, this); }
至此,本次服务构建并发布完成。
2.远程服务暴露(基于注册中心)
这里主要介绍基于 zookeeper的远程服务暴露,
分两步走:
2.1远程服务Invoker创建
2.2发布服务 exporter
2.1 远程服务 Invoker构建
先看源码。ServiceConfig.java
for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic")); 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); } Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter); }
由源码可知服务端在获取到注册中心地址列表后,迭代该列表,
并设置monitor地址【服务监控地址】, export地址【要暴露的服务地址】,接着通过proxtFactory 【JavassistProxyFactory】获取invoker,
最终通过 protocol【url.protocol=registry;因此 protocol = RegistryProtocol】来对服务进行发布【export】
这里注册中心地址列表
registryURL为:
[ registry://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=6720®istry=zookeeper×tamp=1521712841397, registry://10.0.28.54:2182/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=6720®istry=zookeeper×tamp=1521712841397, registry://10.0.28.54:2183/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=6720®istry=zookeeper×tamp=1521712841397 ]
proxyFactory.getInvoker(ref,class,url)
依旧通过JavassistProxyFactory构建Invoker(同1本地服务暴露构建Invoker一直,返回的是新构建的
AbstractProxyInvoker
匿名类,其中
AbstractProxyInvoker
内wrapper 为同一个实例)
此时构建成功的Invoker内的url为包含了export参数的url:
registry://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&export=dubbo%3A%2F%2F10.0.28.54%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D10.0.28.54%26bind.port%3D20880%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D6720%26side%3Dprovider%26timestamp%3D1521713776074&pid=6720®istry=zookeeper×tamp=1521712841397
到这 第一个注册地址 invoker 创建完毕。
2.2 发布远端服务 export()
交互流程如下:
依据日志:
Register dubbo service com.alibaba.dubbo.demo.DemoService url dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.0.28.54&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8956&side=provider×tamp=1522035115024 to registry registry://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=8956®istry=zookeeper×tamp=1522035107435, dubbo version: 2.0.0, current host: 127.0.0.1
来跟踪服务DemoService是如何注册到 zk【10.0.28.54:2181】的。
由于当前url.protocol = registry,因此最终的export发布任务交由RegistryProtocol来处理,
又因为
Protocol
的SPI包含 Wrapper类【 ProtocolListenerWrapper, 管理Filter链的 ProtocolFilterWrapper】,因此protocol.export()类的调用过程为:
ProtocolListenerWrapper.export(invoker) -> ProtocolFilterWrapper.export(invoker) ->RegistryProtocol.export()
接下来我们看看
RegistryProtocol
.export 源码:
export主要干了几件事:
2.2.1.构建Exporter
2.2.2.根据registryUrl获取Registry
2.2.3.registry中注册节点
2.2.4.注册中心订阅url
2.2.5 基于2.2.1的exporter构建心的Exporter并返回
RegistryProtocol.java
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { //export invoker //1.构建Export,并启动NettyServer final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker); URL registryUrl = getRegistryUrl(originInvoker); //获取注册中心地址 //registry provider final Registry registry = getRegistry(originInvoker); //2.获取Registry, final URL registedProviderUrl = getRegistedProviderUrl(originInvoker); // //to judge to delay publish whether or not boolean register = registedProviderUrl.getParameter("register", true); ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl); if (register) { //3.注册服务节点到zookeeper中(创建zk节点) register(registryUrl, registedProviderUrl); ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true); } // 4.订阅override数据 // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); //保证每次export都返回一个新的exporter实例 return new Exporter<T>() { public Invoker<T> getInvoker() { return exporter.getInvoker(); } public void unexport() { try { exporter.unexport(); } catch (Throwable t) { logger.warn(t.getMessage(), t); } try { registry.unregister(registedProviderUrl); } catch (Throwable t) { logger.warn(t.getMessage(), t); } try { overrideListeners.remove(overrideSubscribeUrl); registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener); } catch (Throwable t) { logger.warn(t.getMessage(), t); } } }; } //构建Regisrty,这里会根据regisrtyurl中protocol=zookeeper 进入到ZookeeperRegisrtyFactory中创建Registry,启动zkclient等。 private Registry getRegistry(final Invoker<?> originInvoker) { URL registryUrl = getRegistryUrl(originInvoker); return registryFactory.getRegistry(registryUrl); } //根据protocol构建真正的registryUrl private URL getRegistryUrl(Invoker<?> originInvoker) { URL registryUrl = originInvoker.getUrl(); if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) { String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY); registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY); } return registryUrl; }
2.2.1.构建Exporter
我们先看看doLocalExport都做了什么:
【交互图1中步骤12开始(也就是交互图2)】
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) { /**这里获取provider地址 : *dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-*provider&bind.ip=10.0.28.54&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8956&side=provider×tamp=1522035115024 */ 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) { //这里新构建的Invoker.url为 prividerUrl; 因此url.protocol = dubbo final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker)); //因此这里的protocol.export(invoker) 调用的实例变成了 DubboProtocol exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker); bounds.put(key, exporter); } } } return exporter; }
因此,doLocalExport
主要负责构建Exporter,下面看看是如何使用DubboProtocol构建的。
在跳转到 DubboProtocol 前。会依次经过 【
ProtocolListenerWrapper, 管理Filter链的 ProtocolFilterWrapper
】进行filter包装,最后进入DubboProtocol
DubboProtocol.java
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { public Object reply(ExchangeChannel channel, Object message) throws RemotingException { if (message instanceof Invocation) { Invocation inv = (Invocation) message; Invoker<?> invoker = getInvoker(channel, inv); //如果是callback 需要处理高版本调用低版本的问题 if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) { String methodsStr = invoker.getUrl().getParameters().get("methods"); boolean hasMethod = false; if (methodsStr == null || methodsStr.indexOf(",") == -1) { 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.getContext().setRemoteAddress(channel.getRemoteAddress()); return invoker.invoke(inv); } throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress()); } //当前入参Invoker为经过ProtocolFilterWrapper包装了Filter链之后的Invoker; //阅读该方法可知,主要做几件事: 1.构建Exporter 2.如果是Stub则设置dubbo.stub.event.methods 3.打开NettyServer public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { //dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService? //anyhost=true&application=demo-provider&bind.ip=10.0.28.54&bind.port=20880&dubbo=2.0.0&generic=false& //interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8956&side=provider×tamp=1522035115024 URL url = invoker.getUrl(); // export service. String key = serviceKey(url); //com.alibaba.dubbo.demo.DemoService:20880 DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap); exporterMap.put(key, exporter); //export an stub service for dispaching 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); } } //创建nettySever openServer(url); return exporter; } //创建nettyServer端实例,缓存中存在则返回缓存实例,否则新建Server //缓存serverMap以 host:ip 为key 因此可知,一个服务器ip:port 只会出现一个server private void openServer(URL url) { // find server. String key = url.getAddress(); // ip:port //client 也可以暴露一个只有server可以调用的服务。 boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true); if (isServer) { ExchangeServer server = serverMap.get(key); if (server == null) { serverMap.put(key, createServer(url)); } else { //server支持reset,配合override功能使用 server.reset(url); } } } private ExchangeServer createServer(URL url) { //默认开启server关闭时发送readonly事件 channel.readonly.sent = ture url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString()); //默认开启heartbeat=60000 一分钟 url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT)); String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER); // server=netty 默认使用的是netty if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) throw new RpcException("Unsupported server type: " + str + ", url: " + url); url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME); //设置编解码的方式 此处 codec=dubbo ExchangeServer server; try { //此时url为: //dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService? //anyhost=true&application=demo-provider&bind.ip=10.0.28.54 //&bind.port=20880&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0 //&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello //&pid=8956&side=provider×tamp=1522035115024 server = Exchangers.bind(url, requestHandler); } catch (RemotingException e) { throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e); } str = url.getParameter(Constants.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 server; }创建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,并交由HeaderExchanger处理 这里会进一步封装Handler 【
DecodeHandler(
headerExchangeHandler) ->
HeaderExchangeHandler(
requestHandler)】
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); }
Transporters.bind采用默认的NettyTransporter来构建 NettyServer:
NettyTransporter.java
public Server bind(URL url, ChannelHandler listener) throws RemotingException { return new NettyServer(url, listener); }
接着在构建NettyServer实例的过程中,再次封装Handler链【MultiMessageHandler(
heartbeatHandler) -> HeartbeatHandler(
allChannelHandler) -> AllChannelHandler(
decodeHandler)】
并添加线程池【这里采用FixedThreadPool ->
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit
unit,
BlockingQueue
<
Runnable
> workQueue,
ThreadFactory
threadFactory,
RejectedExecutionHandler
handler)
->
ThreadPoolExecutor(
200,200,0,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>(),new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url))】
请求执行默认超时时间timeout:1000ms, //见父类构造函数AbstractEndpoint
连接默认超时时间connect.timeout:3000ms
获取编解码方式(codec=dubbo)DubboCountCodec
accepts:0,
默认空闲时间idleTimeout:600000 //见父类构造函数AbstractServer
最后调用doOpen开启NettyServer
AbstractServer.java
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); localAddress = getUrl().toInetSocketAddress(); String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost()); int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort()); if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) { bindIp = NetUtils.ANYHOST; } bindAddress = new InetSocketAddress(bindIp, bindPort); this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS); this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT); try { doOpen(); //这里将调用子类启动NettyServer if (logger.isInfoEnabled()) { logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress()); } } catch (Throwable t) { throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName() + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t); } DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension(); executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort())); }
NettyServer.java
protected void doOpen() throws Throwable { NettyHelper.setNettyLoggerFactory(); ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true)); ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true)); ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); //DEFAULT_IO_THREADS=5 bootstrap = new ServerBootstrap(channelFactory); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); channels = nettyHandler.getChannels(); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this); ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); // bind channel = bootstrap.bind(getBindAddress()); }
这里ChannelPipeline handler为 上层构建出的 MultiMessageHandler 经过 NettyServer包装后的handler:
NettyHandler
(NettyServer(
MultiMessageHandler
))
到这,NettyServer创建完毕,原路返回至DubboProtocol.openServer... 返回构建好的DubboExporter....
dubboExporter返回后,RegistryProtocol又做了什么?请看【
交互图1中17步开始(也就是交互图3)】
2.2.2.根据registryUrl获取Registry
我们先看看RegistryProtocol.getRegistry:
private Registry getRegistry(final Invoker<?> originInvoker) { URL registryUrl = getRegistryUrl(originInvoker); return registryFactory.getRegistry(registryUrl); } private URL getRegistryUrl(Invoker<?> originInvoker) { URL registryUrl = originInvoker.getUrl(); if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) { String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY); registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY); } return registryUrl; }
getRegistryUrl会重新设置url的protocol,最后registryUrl为:
zookeeper://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0 &export=dubbo%3A%2F%2F10.0.28.54%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D10.0.28.54%26bind.port%3D20880%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D8956%26side%3Dprovider%26timestamp%3D1522035115024 &pid=8956×tamp=1522035107435
因此
registryFactory.getRegistry(registryUrl) 将交由 ZookeeperRegistryFactory处理。
获取Registry过程交互图如下:
步骤1.3 AbstractRegistry.getRegistry 首先会从缓存Map中查看是否存在 【zookeeper://ip:port/com.alibaba.dubbo.registry.RegistryService】为key的registry值。有则直接返回缓存中的实例,没有则开始创建新的Registry并加入到缓存map中;
AbstractRegistry.java
public Registry getRegistry(URL url) { url = url.setPath(RegistryService.class.getName()) .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()) .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY); String key = url.toServiceString(); // 锁定注册中心获取过程,保证注册中心单一实例 LOCK.lock(); try { Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; } registry = createRegistry(url); if (registry == null) { throw new IllegalStateException("Can not create registry " + url); } REGISTRIES.put(key, registry); return registry; } finally { // 释放锁 LOCK.unlock(); } }
步骤1.4创建Registry:这里根据url.protocol=zookeeper 知,创建的Registry为 ZookeeperRegistry,构造ZookeeperRegistry过程需要经过FailbackRegistry ,AbstractRegistry两个父类构造函数;
ZookeeperRegistryFactory.java
public Registry createRegistry(URL url) { return new ZookeeperRegistry(url, zookeeperTransporter); }
AbstractRegistry.java
public AbstractRegistry(URL url) { setUrl(url); // 启动文件保存定时器 syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false); String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache"); File file = null; if (ConfigUtils.isNotEmpty(filename)) { file = new File(filename); if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) { if (!file.getParentFile().mkdirs()) { throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!"); } } } this.file = file; loadProperties(); notify(url.getBackupUrls()); } private void loadProperties() { if (file != null && file.exists()) { InputStream in = null; try { in = new FileInputStream(file); properties.load(in); if (logger.isInfoEnabled()) { logger.info("Load registry store file " + file + ", data: " + properties); } } catch (Throwable e) { logger.warn("Failed to load registry store file " + file, e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { logger.warn(e.getMessage(), e); } } } } }
首先父级构造函数
AbstractRegistry
会启动本地缓存文件定时器,并加载本地文件
${user.home}/.dubbo/dubbo-registry-${application.name}-${address}.cache
(如
C:\Users\0212149/.dubbo/dubbo-registry-demo-provider-10.0.28.54:2181.cache
)
接着调用notify 对backupUrl进行通知
FailbackRegistry.java
public FailbackRegistry(URL url) { super(url); int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD); // DEFAULT_REGISTRY_RETRY_PERIOD=5000 this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() { public void run() { // 检测并连接注册中心 try { retry(); } catch (Throwable t) { // 防御性容错 logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t); } } }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS); }
父类FailbackRegistry 构造函数主要启动定时器对注册中心进行连接状态检测,断开则重启,检测频率:5s
ZookeeperRegistry.java
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) { super(url); if (url.isAnyHost()) { throw new IllegalStateException("registry address == null"); } String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT); // DEFAULT_ROOT=dubbo if (!group.startsWith(Constants.PATH_SEPARATOR)) { group = Constants.PATH_SEPARATOR + group; } this.root = group; zkClient = zookeeperTransporter.connect(url); zkClient.addStateListener(new StateListener() { public void stateChanged(int state) { if (state == RECONNECTED) { try { recover(); } catch (Exception e) { logger.error(e.getMessage(), e); } } } }); }
ZookeeperRegistry 构造函数将会使用 ZookeeperTransporter$Adatpive 来获取 ZookeeperClient;并添加zk状态监听(主要是连接断开重连)
ZookeeperTransporter$Adaptive 使用默认的zkclient来处理,因此,connect操作最终交由 ZkclientZookeeperTransporter处理:
ZkclientZookeeperTransporter.java
public ZookeeperClient connect(URL url) { return new ZkclientZookeeperClient(url); //构造 一个 ZkclientZookeeperClient 实例 }步骤 1.5创建 ZookeeperClient,ZookeeperClient构造时会通过 构建 ZkClientWrapper 实例来异步(FutureTask)创建ZkClient实例并获取
ZkclientZookeeperClient.java
public ZkclientZookeeperClient(URL url) { super(url); client = new ZkClientWrapper(url.getBackupAddress(), 30000); client.addListener(new IZkStateListener() { public void handleStateChanged(KeeperState state) throws Exception { ZkclientZookeeperClient.this.state = state; if (state == KeeperState.Disconnected) { stateChanged(StateListener.DISCONNECTED); } else if (state == KeeperState.SyncConnected) { stateChanged(StateListener.CONNECTED); } } public void handleNewSession() throws Exception { stateChanged(StateListener.RECONNECTED); } }); client.start(); }
这里创建的是
ZkclientZookeeperClient
,
ZkclientZookeeperClient
构造函数将对client初始化(new
ZkClientWrapper
(),
ZkClientWrapper
返回的是持有 ZkClient
的实例)
ZkClientWrapper.java
//这里构造函数内会创建 新建 ZkClient实例的 future任务 public ZkClientWrapper(final String serverAddr, long timeout) { this.timeout = timeout; listenableFutureTask = ListenableFutureTask.create(new Callable<ZkClient>() { @Override public ZkClient call() throws Exception { return new ZkClient(serverAddr, Integer.MAX_VALUE); } }); } // ZkclientZookeeperClient 构造函数调用 start(),真正执行future任务,构建ZkClient实例。 public void start() { if (!started) { Thread connectThread = new Thread(listenableFutureTask); connectThread.setName("DubboZkclientConnector"); connectThread.setDaemon(true); connectThread.start(); try { client = listenableFutureTask.get(timeout, TimeUnit.MILLISECONDS); } catch (Throwable t) { logger.error("Timeout! zookeeper server can not be connected in : " + timeout + "ms!", t); } started = true; } else { logger.warn("Zkclient has already been started!"); } }
到这里,zkClient 创建完毕。也就是 Registry的构建完毕
2.2.3.注册服务节点到zookeeper中(创建zk节点)
创建节点交互流程如下:
我们先看看源码片段 export 方法在获取到Registry之后接着执行 服务节点注册到zk操作;调用register方法,如下:
public void register(URL registryUrl, URL registedProviderUrl) { Registry registry = registryFactory.getRegistry(registryUrl); registry.register(registedProviderUrl); }
有代码可知,provider将会被发送到zkclient并开始创建目录节点。下面具体分析执行流程
首先registry会点调用父类 AbstractRegistry对服务Url进行缓存, 父类FailbackRegistry 会检测错误注册列表,未注册列表中是否存在即将发布的服务url,如果存在,则删除;
接着调用ZookeeperRegistry发送注册请求(使用zkClient向zk注册节点),如果发生异常,则将url添加至注册失败列表中,定时重试。
AbstractRegistry.java
public void register(URL url) { if (url == null) { throw new IllegalArgumentException("register url == null"); } if (logger.isInfoEnabled()) { logger.info("Register: " + url); } registered.add(url); }
FailbackRegistry.java
public void register(URL url) { if (destroyed.get()){ return; } super.register(url); //调用父类方法将providerUrl缓存起来 failedRegistered.remove(url); //错误注册列表中删除待发布的url failedUnregistered.remove(url);//未注册列表删除待发布url try { // 向服务器端发送注册请求 doRegister(url); } catch (Exception e) { Throwable t = e; // 如果开启了启动时检测,则直接抛出异常 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); } // 将失败的注册请求记录到失败列表,定时重试 failedRegistered.add(url); } }
ZookeeperRegistry.java
//在zk中创建providerUrl节点 //这里的入参url为:dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService? //anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService //&methods=sayHello&pid=1768&side=provider×tamp=1522227208103 protected void doRegister(URL url) { try { // zkClient.create(url,true) 这里true表示创建临时节点,会因为客户端会话的失效而被删除。调用 ZkClientZookeeperClient创建节点 【节点分类详情请看:zookeeper/zookeeper节点类型】 //toUrlPath(url) 将url转换成文件目录结构式的: // /dubbo/com.alibaba.dubbo.demo.DemoService/providers/ // dubbo%3A%2F%2F10.0.28.54%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D1768%26side%3Dprovider%26timestamp%3D1522227208103 zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } }
ZkClientZookeeperClient.java
//path中包含文件分隔符,则说明为文件夹目录结构,尝试创建【如果不存在】(文件夹节点为持久化节点,会一直存在zk中,直到有删除操作来主动删除这个节点)
public void create(String path, boolean ephemeral) {
int i = path.lastIndexOf('/');
if (i > 0) {
String parentPath = path.substring(0, i);
if (!checkExists(parentPath)) {
create(parentPath, false); //递归创建目录节点
}
}
if (ephemeral) {
createEphemeral(path); //最后创建叶子节点 (这里就是 encode(providerUrl))
} else {
createPersistent(path);
}
}
createEphemeral
createPersistent
依次进入
ZkClientZookeeperClient
-> ZkClientWrapper -> ZkClient 创建节点
zk创建节点流程结束。。。。
2.2.4 订阅url,监听zk节点变化
先回顾下2.2.4的代码:
RegistryProtocol.java export
方法有如下片段
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { // 4.订阅override数据 // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl); //首先会根据providerUrl 构建 overrideSubscribeUrl final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); //调用Registry订阅 } private URL getSubscribedOverrideUrl(URL registedProviderUrl) { return registedProviderUrl.setProtocol(Constants.PROVIDER_PROTOCOL) .addParameters(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false)); }
当前providerUrl为:
dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=13860&side=provider×tamp=1522467555446
overrideSubscribeUrl :
provider://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=13860&side=provider×tamp=1522467555446
注意,这里的
overrideSubscribeUrl
protocol已经变成了 provider,且 category只设置了 configurations
接着构建 OverrideListener ,并调用ZookeeperRegistry 进行url订阅,目的是为了在目录下节点数据发生变化时通过zk的事件触发并调用 OverrideListener 重新根据新的参数生成exporter (OverrideListener notify方法)
订阅整个交互如下:
FailbackRegistry.java
public void subscribe(URL url, NotifyListener listener) { if (destroyed.get()){ return; } super.subscribe(url, listener); //AbstractRegisrty removeFailedSubscribed(url, listener); try { // 向服务器端发送订阅请求 doSubscribe(url, listener); //ZookeeperRegistry } catch (Exception e) { Throwable t = e; List<URL> urls = getCacheUrls(url); if (urls != null && urls.size() > 0) { notify(url, listener, urls); logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t); } else { // 如果开启了启动时检测,则直接抛出异常 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t); } else { logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t); } } // 将失败的订阅请求记录到失败列表,定时重试 addFailedSubscribed(url, listener); } }
AbstarctRegistry.java
public void subscribe(URL url, NotifyListener listener) { if (url == null) { throw new IllegalArgumentException("subscribe url == null"); } if (listener == null) { throw new IllegalArgumentException("subscribe listener == null"); } if (logger.isInfoEnabled()) { logger.info("Subscribe: " + url); } Set<NotifyListener> listeners = subscribed.get(url); if (listeners == null) { subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>()); listeners = subscribed.get(url); } listeners.add(listener); }
ZookeeperRegistry.subscribe 实际调用的是父类FailbackRegistry.subscribe(),
FailbackRegistry.subscribe()首先调用父类AbstarctRegistry将该url,listener 保存到已订阅列表
subscribed<url,Set<listener>>
中,
接着从订阅失败列表和未订阅列表中将该url对应的入参listener删除掉,最后调用ZookeeperRegistry.doSubscribe ()方法进行订阅;
ZookeeperRegistry.java
protected void doSubscribe(final URL url, final NotifyListener listener) { try { if (Constants.ANY_VALUE.equals(url.getServiceInterface())) { String root = toRootPath(); ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url); if (listeners == null) { zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>()); listeners = zkListeners.get(url); } ChildListener zkListener = listeners.get(listener); if (zkListener == null) { listeners.putIfAbsent(listener, new ChildListener() { public void childChanged(String parentPath, List<String> currentChilds) { for (String child : currentChilds) { child = URL.decode(child); if (!anyServices.contains(child)) { anyServices.add(child); subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child, Constants.CHECK_KEY, String.valueOf(false)), listener); } } } }); zkListener = listeners.get(listener); } zkClient.create(root, false); List<String> services = zkClient.addChildListener(root, zkListener); if (services != null && services.size() > 0) { for (String service : services) { service = URL.decode(service); anyServices.add(service); subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service, Constants.CHECK_KEY, String.valueOf(false)), listener); } } } else { //执行else流程 List<URL> urls = new ArrayList<URL>(); //这里toCategoriesPath(url)会根据 url.category来构建需要注册到zk中的目录结构列表【格式:${root}/serviceName/category】, // 如category=configurations,则 则会构建成 /dubbo/com.alibaba.dubbo.demo.DemoService/configurators // 接着迭代构建的目录机构,更新zkListener<url,Map<NotifyListener, ChildListener>>,zkListener是以url为key,value为一个Map结构,该Map //结构key为 入参listener( overrideListener) ,value 为构建的zkChildListener; for (String path : toCategoriesPath(url)) { ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url); if (listeners == null) { zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>()); listeners = zkListeners.get(url); } ChildListener zkListener = listeners.get(listener); if (zkListener == null) { listeners.putIfAbsent(listener, new ChildListener() { public void childChanged(String parentPath, List<String> currentChilds) { //这里的ChildListner childChanged时间会在zk的path节点下数据发生变化时触发执行,最后执行的是Zookeeper.notify方法,notify方法会保存新的url到本地服务列表缓存文件中; // 同时会调用 overrideListener将新的url传入,overrideListener的notify方法会根据新的入参url与当前url比对,如果存在差异,则重新构建url的Exporter ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)); } }); zkListener = listeners.get(listener); } zkClient.create(path, false); //创建目录 List<String> children = zkClient.addChildListener(path, zkListener); //这里添加zk上path目录的监听事件 if (children != null) { urls.addAll(toUrlsWithEmpty(url, path, children)); } } notify(url, listener, urls); } } catch (Throwable e) { throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } }
到这里,订阅逻辑完毕。
Exporter也构建完毕。。。。。