public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (! isMatchPackage(bean)) { return bean; } Service service = bean.getClass().getAnnotation(Service.class); if (service != null) { ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service); if (void.class.equals(service.interfaceClass()) && "".equals(service.interfaceName())) { if (bean.getClass().getInterfaces().length > 0) { serviceConfig.setInterface(bean.getClass().getInterfaces()[0]); } else { throw new IllegalStateException("Failed to export remote service class " + bean.getClass().getName() + ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces."); } } if (applicationContext != null) { serviceConfig.setApplicationContext(applicationContext); if (service.registry() != null && service.registry().length > 0) { List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>(); for (String registryId : service.registry()) { if (registryId != null && registryId.length() > 0) { registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class)); } } serviceConfig.setRegistries(registryConfigs); } if (service.provider() != null && service.provider().length() > 0) { serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(),ProviderConfig.class)); } if (service.monitor() != null && service.monitor().length() > 0) { serviceConfig.setMonitor((MonitorConfig)applicationContext.getBean(service.monitor(), MonitorConfig.class)); } if (service.application() != null && service.application().length() > 0) { serviceConfig.setApplication((ApplicationConfig)applicationContext.getBean(service.application(), ApplicationConfig.class)); } if (service.module() != null && service.module().length() > 0) { serviceConfig.setModule((ModuleConfig)applicationContext.getBean(service.module(), ModuleConfig.class)); } if (service.provider() != null && service.provider().length() > 0) { serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(), ProviderConfig.class)); } else { } if (service.protocol() != null && service.protocol().length > 0) { List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>(); for (String protocolId : service.registry()) { if (protocolId != null && protocolId.length() > 0) { protocolConfigs.add((ProtocolConfig)applicationContext.getBean(protocolId, ProtocolConfig.class)); } } serviceConfig.setProtocols(protocolConfigs); } try { serviceConfig.afterPropertiesSet(); } catch (RuntimeException e) { throw (RuntimeException) e; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } serviceConfig.setRef(bean); serviceConfigs.add(serviceConfig); serviceConfig.export(); } return bean; }
首先得到服务提供类的Service注解,再封装成ServiceConfig,再注入类的接口等一些配置信息,其实setRef(bean)就是把真正服务实现类的实例传入,再把ServiceConfig添加到本地的ServiceConfig集合中,最后再调用ServiceConfig的export()暴露服务。
我们先看下封装成serverConfig的过程。即ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);具体逻辑在父类ServerConfig的构造方法中。调用了appendAnnotation()方法。
protected void appendAnnotation(Class<?> annotationClass, Object annotation) { Method[] methods = annotationClass.getMethods(); for (Method method : methods) { if (method.getDeclaringClass() != Object.class && method.getReturnType() != void.class && method.getParameterTypes().length == 0 && Modifier.isPublic(method.getModifiers()) && ! Modifier.isStatic(method.getModifiers())) { try { String property = method.getName(); if ("interfaceClass".equals(property) || "interfaceName".equals(property)) { property = "interface"; } String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); Object value = method.invoke(annotation, new Object[0]); if (value != null && ! value.equals(method.getDefaultValue())) { Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType()); if ("filter".equals(property) || "listener".equals(property)) { parameterType = String.class; value = StringUtils.join((String[]) value, ","); } else if ("parameters".equals(property)) { parameterType = Map.class; value = CollectionUtils.toStringMap((String[]) value); } try { Method setterMethod = getClass().getMethod(setter, new Class<?>[] { parameterType }); setterMethod.invoke(this, new Object[] { value }); } catch (NoSuchMethodException e) { // ignore } } } catch (Throwable e) { logger.error(e.getMessage(), e); } } } }
通过反射调用setter方法,将Service注解上的值注入到新生成的ServiceConfig实例中。(实现很巧妙,让人不禁多看几眼)
继续看ServiceConfig的export()方法。判断是否有export,并是否延迟暴露,最后调用doExport();又是一堆的检查,在这最后调用doExportUrls();
private void doExportUrls() { //将注册的所有url匹配上对应的协议在服务端暴露出来 /** * 将注册协议转化成url * registry://45.119.68.23:2181/com.alibaba.dubbo.registry.RegistryService? * application=test-dubbo&dubbo=2.5.3&pid=7648®istry=zookeeper×tamp=1462349748801 */ List<URL> registryURLs = loadRegistries(true); //配置多通信协议时,都进行暴露 for (ProtocolConfig protocolConfig : protocols) { doExportUrlsFor1Protocol(protocolConfig, registryURLs); } }
先调用loadRegistries()方法,对应每个注册中心生成匹配的URL的数据结构(URL即protocol+map形式)
registry://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=test-protocol-random-port&dubbo=2.0.0 &pid=4324®istry=dubbo×tamp=1527428955474上面是registryURLs的一个例子
protected List<URL> loadRegistries(boolean provider) { checkRegistry(); List<URL> registryList = new ArrayList<URL>(); if (registries != null && registries.size() > 0) { for (RegistryConfig config : registries) { String address = config.getAddress(); if (address == null || address.length() == 0) { address = Constants.ANYHOST_VALUE; } String sysaddress = System.getProperty("dubbo.registry.address"); if (sysaddress != null && sysaddress.length() > 0) { address = sysaddress; } if (address != null && address.length() > 0 && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) { Map<String, String> map = new HashMap<String, String>(); appendParameters(map, application); appendParameters(map, config); map.put("path", RegistryService.class.getName()); map.put("dubbo", Version.getVersion()); map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); if (ConfigUtils.getPid() > 0) { map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid())); } if (! map.containsKey("protocol")) { if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) { map.put("protocol", "remote"); } else { map.put("protocol", "dubbo"); } } List<URL> urls = UrlUtils.parseURLs(address, map); for (URL url : urls) { url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol()); url = url.setProtocol(Constants.REGISTRY_PROTOCOL); if ((provider && url.getParameter(Constants.REGISTER_KEY, true)) || (! provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) { registryList.add(url); } } } } } return registryList; }
既然有多种通信协议,那么遍历所有不同的协议,都进行服务暴露(因为dubbo支持多通信协议时,都进行暴露)。
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { //如果没配置protocol则默认使用dubbo协议 String name = protocolConfig.getName(); if (name == null || name.length() == 0) { name = "dubbo"; } //获取主机地址 String host = protocolConfig.getHost(); if (provider != null && (host == null || host.length() == 0)) { host = provider.getHost(); } boolean anyhost = false; if (NetUtils.isInvalidLocalHost(host)) { anyhost = true; try { host = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { logger.warn(e.getMessage(), e); } if (NetUtils.isInvalidLocalHost(host)) { if (registryURLs != null && registryURLs.size() > 0) { for (URL registryURL : registryURLs) { try { //创建socket,连接到注册中心 Socket socket = new Socket(); try { SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort()); socket.connect(addr, 1000); //获取服务所在主机地址 host = socket.getLocalAddress().getHostAddress(); break; } finally { try { socket.close(); } catch (Throwable e) {} } } catch (Exception e) { logger.warn(e.getMessage(), e); } } } if (NetUtils.isInvalidLocalHost(host)) { host = NetUtils.getLocalHost(); } } } //获取协议接口号 Integer port = protocolConfig.getPort(); if (provider != null && (port == null || port == 0)) { port = provider.getPort(); } final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort(); if (port == null || port == 0) { port = defaultPort; } if (port == null || port <= 0) { port = getRandomPort(name); if (port == null || port < 0) { port = NetUtils.getAvailablePort(defaultPort); putRandomPort(name, port); } logger.warn("Use random available port(" + port + ") for protocol " + name); } //获取application、module、provider、protocol、exporter、registries、monitor所有属性 Map<String, String> map = new HashMap<String, String>(); if (anyhost) { map.put(Constants.ANYHOST_KEY, "true"); } map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE); map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion()); map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); if (ConfigUtils.getPid() > 0) { map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid())); } /** * 将不为null的配置对象中的属性设置到 map 中 * 即将 xml 配置文件中的配置设置的值全转化成为map * {side=provider, application=alijk-dubbo, accepts=1000, * dubbo=2.5.3, threads=100, pid=7236, interface=cn.eoncloud.account.sdk.export.AccountService, * threadpool=fixed, version=1.0.0, timeout=500, anyhost=true, timestamp=1462347843960} */ appendParameters(map, application); appendParameters(map, module); appendParameters(map, provider, Constants.DEFAULT_KEY); appendParameters(map, protocolConfig); appendParameters(map, this); if (methods != null && methods.size() > 0) { for (MethodConfig method : methods) { appendParameters(map, method, method.getName()); String retryKey = method.getName() + ".retry"; if (map.containsKey(retryKey)) { String retryValue = map.remove(retryKey); if ("false".equals(retryValue)) { map.put(method.getName() + ".retries", "0"); } } List<ArgumentConfig> arguments = method.getArguments(); if (arguments != null && arguments.size() > 0) { for (ArgumentConfig argument : arguments) { //类型自动转换. if(argument.getType() != null && argument.getType().length() >0){ Method[] methods = interfaceClass.getMethods(); //遍历所有方法 if(methods != null && methods.length > 0){ for (int i = 0; i < methods.length; i++) { String methodName = methods[i].getName(); //匹配方法名称,获取方法签名. if(methodName.equals(method.getName())){ Class<?>[] argtypes = methods[i].getParameterTypes(); //一个方法中单个callback if (argument.getIndex() != -1 ){ if (argtypes[argument.getIndex()].getName().equals(argument.getType())){ appendParameters(map, argument, method.getName() + "." + argument.getIndex()); }else { throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType()); } } else { //一个方法中多个callback for (int j = 0 ;j<argtypes.length ;j++) { Class<?> argclazz = argtypes[j]; if (argclazz.getName().equals(argument.getType())){ appendParameters(map, argument, method.getName() + "." + j); if (argument.getIndex() != -1 && argument.getIndex() != j){ throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType()); } } } } } } } }else if(argument.getIndex() != -1){ appendParameters(map, argument, method.getName() + "." + argument.getIndex()); }else { throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>"); } } } } // end of methods for } if (ProtocolUtils.isGeneric(generic)) { map.put("generic", generic); map.put("methods", Constants.ANY_VALUE); } else { String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { map.put("revision", revision); } String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if(methods.length == 0) { logger.warn("NO method found in service interface " + interfaceClass.getName()); map.put("methods", Constants.ANY_VALUE); } else { map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); } } if (! ConfigUtils.isEmpty(token)) { if (ConfigUtils.isDefault(token)) { map.put("token", UUID.randomUUID().toString()); } else { map.put("token", token); } } if ("injvm".equals(protocolConfig.getName())) { protocolConfig.setRegister(false); map.put("notify", "false"); } // 导出服务 String contextPath = protocolConfig.getContextpath(); if ((contextPath == null || contextPath.length() == 0) && provider != null) { contextPath = provider.getContextpath(); } /** * 将配置信息转化成 url ,主要根据之前map里的数据组装成url * 调用 URL#buildString方法 * dubbo://10.6.13.137:9998/cn.eoncloud.account.sdk.export.AccountService * ?accepts=1000&anyhost=true&application=test-dubbo&dubbo=2.5.3 * &interface=cn.eoncloud.account.sdk.export.AccountService * &methods=getAccountName,getAllTest&pid=7236&revision=1.0.0&side=provider * &threadpool=fixed&threads=100&timeout=500×tamp=1462347843960&version=1.0.0 */ URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map); if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .hasExtension(url.getProtocol())) { url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .getExtension(url.getProtocol()).getConfigurator(url).configure(url); } String scope = url.getParameter(Constants.SCOPE_KEY); //配置为none不暴露 if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务) if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { exportLocal(url); } //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露本地服务) if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){ if (logger.isInfoEnabled()) { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) { for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic")); // 配置的了monitor加载monitor,并给URL设置MONITOR_KEY URL monitorUrl = loadMonitor(registryURL); if (monitorUrl != null) { url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); } if (logger.isInfoEnabled()) { logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); } //获取invoker;根据服务具体实现,实现接口以及regitryUrl从代理工厂ProxyFactory获取代理Invoker(继承于AbstractProxyInvoker),它是对具体实现的一种代理 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); //根据协议将invoker暴露成exporter,具体过程是创建一个ExchangeServer,它会绑定一个ServerSocket到配置端口 //protocol.export(invoker); 这个protocol的值为:RegistryProtocol,也就是暴露会跳到:RegistryProtocol.exprot中去处理 Exporter<?> exporter = protocol.export(invoker); //将创建的exporter放进链表便于管理 exporters.add(exporter); } } else { Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); } } } this.urls.add(url); }
这里主要将相关的配置转化成map,然后将map跟不同的protocol生成URL(上文介绍过,这是Dubbo特有的数据结构)。最终暴露的dubbo服务也就是这个统一的url,这个url也会注册到zookeeper的节点上。
dubbo://192.168.67.174:20880/com.alibaba.dubbo.config.api.DemoService?anyhost=true&application=test-protocol-random-port &bind.ip=192.168.67.174&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.config.api.DemoService &methods=sayName,getUsers,echo,getBox,throwDemoException&pid=12732&side=provider×tamp=1527429609471上面是拉出来的一个URL的例子,可以看到dubbo为Protocol,后面跟着host地址,再后面是所需要实现的服务端的接口类型,后面是一个map。
//获取invoker;根据服务具体实现,实现接口以及regitryUrl从代理工厂ProxyFactory获取代理Invoker(继承于AbstractProxyInvoker),它是对具体实现的一种代理 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); //根据协议将invoker暴露成exporter,具体过程是创建一个ExchangeServer,它会绑定一个ServerSocket到配置端口 //protocol.export(invoker); 这个protocol的值为:RegistryProtocol,也就是暴露会跳到:RegistryProtocol.exprot中去处理 Exporter<?> exporter = protocol.export(invoker); //将创建的exporter放进链表便于管理 exporters.add(exporter);
然后根据不同的注册中心,不同的协议调用,先不同的protocol的export();配置有filter或者listener的情况下,会在这里产生关于具体服务暴露操作的过滤与监听。值得一提的是,如果采用的是registry协议,那么并不会经过ProtocolListenerWrapper的监听,而是直接进入export()方法开始服务的暴露。这里protocol是registryProtocol,我们来看下RegistryProtocol.exprot()。注释强调了,主要做两件事:1、开启netty服务端 。2、创建zookeeper服务节点。
/** * 主要做两件事情:1、开启netty服务端 。2、创建zookeeper服务节点 */ public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { //export invoker doLocalExport调用dubboProtocol.export开启netty服务监听 final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker); //registry provider final Registry registry = getRegistry(originInvoker); final URL registedProviderUrl = getRegistedProviderUrl(originInvoker); // 调用zodoRegister的doRegister 创建zookeeper的服务节点 registry.register(registedProviderUrl); // 订阅override数据 // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl); 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); } } }; }首先通过doLocalExport()方法开始本地服务的暴露。
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){ String key = getCacheKey(originInvoker); ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key); if (exporter == null) { synchronized (bounds) { exporter = (ExporterChangeableWrapper<T>) bounds.get(key); if (exporter == null) { final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker)); //此处protol为dubboProtocol // dubboProtocol的export对服务进行暴露,这个export最终目的就是开启netty的监听 exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker); bounds.put(key, exporter); } } } return (ExporterChangeableWrapper<T>) exporter; }先根据invoker获取提供方的url来得到key,然后根据key来尝试获得ExporterChangeableWrapper,如果没有获得那么传入invoker跟提供方的url来构造InbokerDelegete,再通过dubboProtocol的export对服务进行暴露。我们来看具体方法。
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { URL url = invoker.getUrl(); // export service. String key = serviceKey(url); DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap); exporterMap.put(key, exporter); //export an stub service for 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); } } openServer(url); return exporter; }先得到服务提供者的url,再根据url构造成server key,再构造DubboExporter并根据key和export存放在exporterMap中。如果是远程服务并且支持回调,则加入stubServiceMethodsMap中。关键是openServer()方法,继续看。
private void openServer(URL url) { // find server. String key = url.getAddress(); //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); } } }先根据server key得到server,如果已经有server(即已经建立过于服务器的连接),那么直接返回,否则通过createServer()与注册中心建立网络连接。并且加入serverMap,下次握有相同key,则直接返回。
private ExchangeServer createServer(URL url) { //默认开启server关闭时发送readonly事件 url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString()); //默认开启heartbeat url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT)); //默认使用netty String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER); if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) throw new RpcException("Unsupported server type: " + str + ", url: " + url); //默认使用dubbo协议编码 url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME); ExchangeServer server; try { //HeaderExchangeServer 在此处已经开启了Netty Server 进行监听 // Exchangers.bind(url, requestHandler) 默认为:HeaderExchanger.bind() 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; }
一些默认的处理,关键是Exchangers.bind()方法,返回了ExchangeServer(介个貌似已经是netty server的监听服务器,也就是说这里完成了对网络监听的配置与开启)我们来看下具体的流程。跟进去:
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange"); return getExchanger(url).bind(url, handler); public static Exchanger getExchanger(URL url) { String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);//header return getExchanger(type); }
可以看到这里默认是headerExchange,那么继续去看它的bind方法
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { //Transporters默认为NettyTransporter return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); }我们再看看Transports的bind方法
public static Server 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(); }通过SPI的方法,获得transport的适配类,再调用其bind方法,具体默认实现类是NettyTansporter。
public static final String NAME = "netty"; public Server bind(URL url, ChannelHandler listener) throws RemotingException { return new NettyServer(url, listener); }
在这里我们看到其根据url建立NettyServer,并设置了回调listener,就是之前的requestHandler。
NettyServer的父类构造中调用了NettyServer的doOpen()
protected void doOpen() throws Throwable { NettyHelper.setNettyLoggerFactory(); ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true)); ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true)); //最后一个参数为 NIO 最大工作线程数 ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); //netty server 启动器 bootstrap = new ServerBootstrap(channelFactory); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); channels = nettyHandler.getChannels(); // https://issues.jboss.org/browse/NETTY-365 // https://issues.jboss.org/browse/NETTY-379 // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true)); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this); ChannelPipeline pipeline = Channels.pipeline(); /*int idleTimeout = getIdleTimeout(); if (idleTimeout > 10000) { pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0)); }*/ pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); // bind ; 创建一个绑定到指定地址的新通道,也就是绑定IP、端口供客户端连接 channel = bootstrap.bind(getBindAddress()); }
这里就是很熟悉的netty服务端的开启,其中nettyHandler是处理业务逻辑层,它是处理客户端连接,消息收发等。
netty服务端开启后,我们回到HeaderExchangeServer的构造中,得到netty服务器后,开启心跳监测机制。
final Registry registry = getRegistry(originInvoker); final URL registedProviderUrl = getRegistedProviderUrl(originInvoker); // 调用zodoRegister的doRegister 创建zookeeper的服务节点 registry.register(registedProviderUrl); // 订阅override数据 // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key, 导致订阅信息覆盖。 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl); overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); //订阅 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
回到RegistryProtocol.exprot()中继续,通过getRegistry()获得注册中心的实例,会根据配置的registryFactory生成对应的registry实例(这里是zookeeperRegister)。得到zookeeper注册中心的URL,通过register()的方式向注册创建zk节点,之后,向注册中心订阅这一服务,以保证注册数据变动时的自动推送。
getRegistry()使用RegistryFactory的getRegistry(),实际上是调用Zookeeper的ceateRegistry()方法(根据配置,使用zookeeper注册中心为例子)。
public Registry createRegistry(URL url) { return new ZookeeperRegistry(url, zookeeperTransporter); }
我们继续来看
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); 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); } } } }); }
建立了zkClient。
我们再来看registry.register()方法的具体逻辑
@Override public void register(URL url) { super.register(url); failedRegistered.remove(url); failedUnregistered.remove(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); } } //zookeeper类 protected void doRegister(URL url) { try { 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); } }
可以看到这里创建了zk节点。
我们继续回到前面的doExportUrlsFor1Protocol中
//获取invoker;根据服务具体实现,实现接口以及regitryUrl从代理工厂ProxyFactory获取代理Invoker(继承于AbstractProxyInvoker),它是对具体实现的一种代理 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); //根据协议将invoker暴露成exporter,具体过程是创建一个ExchangeServer,它会绑定一个ServerSocket到配置端口 //protocol.export(invoker); 这个protocol的值为:RegistryProtocol,也就是暴露会跳到:RegistryProtocol.exprot中去处理 Exporter<?> exporter = protocol.export(invoker); //将创建的exporter放进链表便于管理 exporters.add(exporter);
我们分析了Protocol.export这个方法,现在本地建立netty server监听,再将invoker服务以URL的数据格式,在zookeeper注册中心新建一个zk节点,记录其中。
那么具体的服务是如何封装在invoker中?我们从Invoker<?> invoker = proxyFactory.getInvoker();中入手。默认的是javassistProxyFactory
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO 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类。
在getWrapper方法中,根据需要包装的类来WRAPPER_MAP中寻找,如果没有,则调用makeWrapper,并以需要包装的类为key,得到的wrapper为value存入WRAPPER_MAP中。
makeWrapper()方法根据传入所要包装的类动态生成新的包装类。之后调用了该包装类的invokerMethod方法。我们来重点看下makeWrapper()的关于invokeMethod()方法的生成部分。
if( hasMethod ){ c3.append(" try{"); } for( Method m : methods ) { if( m.getDeclaringClass() == Object.class ) //ignore Object's method. continue; String mn = m.getName(); c3.append(" if( \"").append(mn).append("\".equals( $2 ) "); int len = m.getParameterTypes().length; c3.append(" && ").append(" $3.length == ").append(len); boolean override = false; for( Method m2 : methods ) { if (m != m2 && m.getName().equals(m2.getName())) { override = true; break; } } if (override) { if (len > 0) { for (int l = 0; l < len; l ++) { c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"") .append(m.getParameterTypes()[l].getName()).append("\")"); } } } c3.append(" ) { "); if( m.getReturnType() == Void.TYPE ) c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");") .append(" return null;"); else c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");"); c3.append(" }"); mns.add(mn); if( m.getDeclaringClass() == c ) dmns.add(mn); ms.put(ReflectUtils.getDesc(m), m); } if( hasMethod ){ c3.append(" } catch(Throwable e) { " ); c3.append(" throw new java.lang.reflect.InvocationTargetException(e); " ); c3.append(" }"); } c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");通过javassist的方式动态构造字符串构造的方法体,当外部调用被包装的类的相应的方法,都会在这里被找到方法并调用。我们来看下构造的invokerMethod()的大致样子
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException { dubbo.provider.hello.service.impl.HelloWorld w; try { w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1); } catch (Throwable e) { throw new IllegalArgumentException(e); } try { if ("helloWorld".equals($2) && $3.length == 0) { w. helloWorld (); return null; } if ("getName".equals($2) && $3.length == 0) { return ($w)w. getNanme (); } if ("setName".equals($2) && $3.length == 1) { return ($w)w. setNanme ($4); } } catch (Throwable e) { throw new java.lang.reflect.InvocationTargetException(e); } throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class dubbo.provider.hello.service.impl.HelloServiceImpl."); }在得到了包装类之后,我们回到javassistProxyFactory的getInvoker()方法中,返回一个新生成的AbstractProxyInvoker(),重写了其中的doInvoke()方法,doInvoke方法中调用的是刚才得到的wrapper的invokeMethod()方法(即通过反射调用了传入的所要代理的实例的对应传入的方法名、参数的指定方法)。