[dubbo 源码之 ]1. 效劳提供方怎样宣布效劳
JVM类加载器是否可以加载自定义的String
效劳宣布
启动流程
1.ServiceConfig#export
效劳供应方在启动布置时,dubbo会挪用ServiceConfig#export
来激活效劳宣布流程,以下所示:
- Java API:
// 1. 建立ServiceConfig实例
ServiceConfig<IGreetingService> serviceConfig = new ServiceConfig<>();
// 2. 设置应用程序设置
serviceConfig.setApplication(new ApplicationConfig("deep-in-dubbo-first-provider"));
// 3. 设置注册中间
RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181/");
serviceConfig.setRegistry(registryConfig);
// 4. 设置接口和完成类
// 5. 设置效劳分组和版本
// dubbo中,效劳接口+效劳分组+效劳版本 唯一的肯定一个效劳,统一个接口能够有差别版本,轻易保护升级
serviceConfig.setInterface(IGreetingService.class);
serviceConfig.setRef(new GreetingServiceImpl());
serviceConfig.setVersion("1.0.0");
serviceConfig.setGroup("dubbo-sxzhongf-group");
RpcContext.getContext().setAttachment("age","18");
// 7. 导出效劳,启动Netty监听链接要求,并将效劳注册到注册中间
serviceConfig.export();
// 8. 挂起线程,防止效劳住手
System.out.println("api provider service is started...");
System.in.read();
- XML
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="first-xml-provider"/>
<!-- use multicast registry center to export service -->
<dubbo:registry address="zookeeper://127.0.0.1:2181/"/>
<!-- use dubbo protocol to export service on port 20880 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="com.sxzhongf.deep.in.dubbo.provider.service.impl.GreetingServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="com.sxzhongf.deep.in.dubbo.api.service.IGreetingService"
ref="demoService" version="1.0.0" group="dubbo-sxzhongf-group">
<dubbo:method name="sayHello" async="false" timeout="0" retries="3"></dubbo:method>
<dubbo:method name="testGeneric" async="false" timeout="10000" retries="3"></dubbo:method>
</dubbo:service>
</beans>
检察export
源码可知,总共有三种效劳导出选项:
java public synchronized void export() { //1. 是不是导出 if (!shouldExport()) { return; } ... //2.耽误导出 if (shouldDelay()) { DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS); } else { //3.马上导出 doExport(); } }
2.ServiceConfig#doExport
此要领主假如依据设置的属性举行合法性搜检,重要包括是不是已被导出,doExportUrls();
3.doExportUrls
4.ConfigValidationUtils#loadRegistries
此要领用来加载一切的效劳注册中间对象,在dubbo中,一个service能够被注册到多个注册中间。
经由历程
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
5.doExportUrlsFor1Protocol
在此要领中会将一切的参数封装成
org.apache.dubbo.common.URL
对象,然后实行详细的效劳导出。
详细历程分为:
- 1.剖析MethodConfig设置(零丁的要领挪用参数设置)
- 2.泛型挪用范例设置
- 3.拼接URL参数
- 4.导出详细效劳
导出又分为四种局限(
scope
):- SCOPE_NONE = "none",假如设定为none,示意该效劳不导出。
- SCOPE_LOCAL = "local" ,假如设定为local,示意该效劳导出到当地(injvm--伪协定,完成类为:
org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
)- SCOPE_REMOTE = "remote",假如设定为remote,示意该效劳导出到长途。
- 假如有注册中间,宣布到注册中间
- 假如没有注册中间,则示意效劳是直连体式格局
- 从
dubbo-2.7.0
入手下手,新增加了WritableMetadataService
来存储dubbo 效劳的元数据,元数据能够存储在远端设置中间和当地,默许是存储在当地,经由历程设置:METADATA_KEY = "metadata"
- DEFAULT_METADATA_STORAGE_TYPE = "local"
- REMOTE_METADATA_STORAGE_TYPE = "remote"
java /** * @since 2.7.0 * ServiceData Store */ WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE)); if (metadataService != null) { metadataService.publishServiceDefinition(url); }
- 不设置,导出到当地和远端
- 终究实行导出的代码以下
// 扩大适配类 private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); /** * A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its * default implementation */ // 扩大适配类 private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); ... Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter);
由于
protocol
和PROXY_FACTORY
都是扩大适配类,跟踪代码我们能够发明:- 实行
PROXY_FACTORY.getInvoker
的时刻实际上起首实行扩大接口ProxyFactory
的适配类ProxyFactory$Adaptive
的getInvoker
要领,依据URL
中参数proxy
的设置范例挑选详细的代办工场,默许运用的是javassist
,,因而又挪用了org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getInvoker
来猎取代办完成类,代码以下:/** * JavaassistRpcProxyFactory */ public class JavassistProxyFactory extends AbstractProxyFactory { ... @Override public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO Wrapper cannot handle this scenario correctly: the classname contains '$' // 这里运用javassist动态代办生成serviceImpl完成类的包装类`Wraaper...` 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); } }; } ... }
上面代码有2个目标:
inal Wrapper wrapper = Wrapper.getWrapper(...);
用来生成详细serviceImpl
的包装类,削减反射的机能消耗;return new AbstractProxyInvoker<T>...
返回了一个笼统的代办invoker
,而且重写了doInvoker
要领,重写以后运用包装类中的invokeMethod
来挪用要领。
经由上述2步,效劳供应方就将详细的完成类转换为
Invoker
代办。 - 然后,当实行
protocol.export()
,实际上也是挪用了Protocol$Adaptive#export()
要领,同时也分为两种状况- 假如为长途暴露,则实行
RegistryProtocol#export
- 假如为当地暴露,则只需
InjvmProtocol#export
由于dubbo的
加强SPI
特征支撑,injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
,则在挪用之前会一层一层挪用,ProtocolFilterWrapper
->ProtocolListenerWrapper
->QosProtocolWrapper
,末了会挪用export
要领,此要领会将Invoker
转换为Exporter
对象,在org.apache.dubbo.registry.integration.RegistryProtocol#export
要领中,org.apache.dubbo.registry.integration.RegistryProtocol#doLocalExport
要领启NettyServer
来监听效劳,org.apache.dubbo.registry.integration.RegistryProtocol#register
将当前的效劳注册到注册中间。doLocalExport
是怎样启动NettyServer
呢?private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) { String key = getCacheKey(originInvoker); return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> { Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl); return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker); }); }
此时
URL
中的protocol
范例为默许的dubbo
,因而会实行DubboProtocol#export
举行转换,以下:@Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { URL url = invoker.getUrl(); // export service. String key = serviceKey(url); // invoker->exporter 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.")); } } else { stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods); } } //建立server openServer(url); //序列化提醒 optimizeSerialization(url); return exporter; }
能够看到代码实行到
openServer
,由于key=getAddress()=ip+port
,因而,统一台机械只会开启一个NettyServer
.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)); } } } else { // server supports reset, use together with override server.reset(url); } } }
关于
org.apache.dubbo.remoting.Transporter
的适配类挑选有三种:MinaTransporter
、NettyTransporter
、GrizzlyTransporter
,关于JavaNIO:Apache Mina、JBoss Netty、Sun Grizzly 框架对照:- NettyServer启动以后,回到
org.apache.dubbo.registry.integration.RegistryProtocol#export
要领,继续实即将效劳注册到注册中间,我们以Zookeeper
为例:- 1.起首查找一切注册中间
final Registry registry = getRegistry(originInvoker); ... protected Registry getRegistry(final Invoker<?> originInvoker) { URL registryUrl = getRegistryUrl(originInvoker); return registryFactory.getRegistry(registryUrl); }
由于
RegistryFactory
是一个SPI扩大接口,代码中设置的为zookeeper
,因而这里挪用的是ZookeeperRegistryFactory
,继续自:org.apache.dubbo.registry.support.AbstractRegistryFactory#getRegistry(org.apache.dubbo.common.URL)
,在此要领中挪用了createRegistry
,然则ZookeeperRegistryFactory
重写了createRegistry
,因而详细挪用的是ZookeeperRegistryFactory#createRegistry
,该要领返回了一个new ZookeeperRegistry(url, zookeeperTransporter)
实例对象。 - 2.入手下手注册,
RegistryProtocol#register
要领实行注册行动,起首猎取到我们在上一步找到的注册中间ZookeeperRegistry
,ZookeeperRegistry
实行父类org.apache.dubbo.registry.support.FailbackRegistry#register
,在该要领中会挪用笼统要领:doRegister
,ZookeeperRegistry
重写了改要领,则实行ZookeeperRegistry#doRegister
,以下:@Override public void doRegister(URL url) { try { zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } }
- 3.
toUrlPath
要领会把org.apache.dubbo.common.URL
转换花样后存储到zookeeper,以下:dubbo://172.16.44.21:20880/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService?anyhost=true&application=deep-in-dubbo-first-provider&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=dubbo-sxzhongf-group&interface=com.sxzhongf.deep.in.dubbo.api.service.IGreetingService&methods=sayHello,testGeneric&pid=8480&release=2.7.5&revision=1.0.0&side=provider×tamp=1582872610313&version=1.0.0 -----------------------转换------------------------ /dubbo/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService/providers/dubbo%3A%2F%2F172.16.44.21%3A20880%2Fcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%3Fanyhost%3Dtrue%26application%3Ddeep-in-dubbo-first-provider%26default%3Dtrue%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Ddubbo-sxzhongf-group%26interface%3Dcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%26methods%3DsayHello%2CtestGeneric%26pid%3D8480%26release%3D2.7.5%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1582872610313%26version%3D1.0.0
转换以后的花样实在就是我们在zookeeper中看到的一样了,不过有几个目次:
- dubbo
- com.sxzhongf.deep.in.dubbo.api.service.IGreetingService
- providers
[zk: localhost:2181(CONNECTED) 2] ls /dubbo/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService/providers [dubbo%3A%2F%2F172.16.44.21%3A20880%2Fcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%3Fanyhost%3Dtrue%26application%3Ddeep-in-dubbo-first-provider%26default%3Dtrue%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Ddubbo-sxzhongf-group%26interface%3Dcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%26methods%3DsayHello%2CtestGeneric%26pid%3D15716%26release%3D2.7.5%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1582872850187%26version%3D1.0.0]
- 1.起首查找一切注册中间
- 假如为长途暴露,则实行
- 实行
至此,效劳花费端就能够从注册中间猎取效劳供应service举行挪用了,下节我们继续来剖析,花费端是怎样从注册中间拉取service举行处置惩罚的。
工作五年的.neter的一些经历感想和对未来的一些疑惑