前言
《dubbo之集群》一文所提到功能已经基本完善,dubbo如何进一步封装,使用之简单易用呢?
服务代理
通过《浅谈AOP》中的Javassist技术,dubbo为我们简化代码如下
public class ProxyTest {
public static void main(String[] args) throws Exception {
ServiceRepository repository = ApplicationModel.getServiceRepository();
repository.registerService(GreetingsService.class);
URL url = URL.valueOf("dubbo://127.0.0.1:28092/dubbo.GreetingsService?monitor=mm&timeout=12000");
Protocol protocol = new ProtocolFilterWrapper(new DubboProtocol());
protocol.export(new JdkProxyFactory().getInvoker(new GreetingsServiceImpl(), GreetingsService.class, url));
Invoker<GreetingsService> invoker = protocol.refer(GreetingsService.class, url);
invoker = Cluster.getCluster(null, false).join(new StaticDirectory(Lists.newArrayList(invoker)));
GreetingsService greetingsService = new JdkProxyFactory().getProxy(invoker, new Class[]{ GreetingsService.class});
System.out.println( greetingsService.sayHi("yoyo"));
}
}
服务配置
dubbo进一步通过配置的形势缩小代码,demo如下
public class ConfigTest {
public static void main(String[] args) throws InterruptedException {
// 基本信息全局配置
ConfigManager configManager = ApplicationModel.getConfigManager();
configManager.setApplication(new ApplicationConfig("test"));
// 注册中心全局配置
RegistryConfig registryConfig = new RegistryConfig("127.0.0.1", "zookeeper");
registryConfig.setPort(2181);
configManager.addRegistry(registryConfig);
// 服务提供配置
ServiceConfig<GreetingsService> serviceConfig = new ServiceConfig();
serviceConfig.setInterface(GreetingsService.class);
serviceConfig.setRef(new GreetingsServiceImpl());
serviceConfig.export();
// 服务调用配置
ReferenceConfig<GreetingsService> referenceConfig = new ReferenceConfig();
referenceConfig.setInterface(GreetingsService.class);
referenceConfig.setProtocol("dubbo");
GreetingsService greetingsService = referenceConfig.get();
System.out.println(greetingsService.sayHi("rrrrrr"));
}
}
除了上述JavaBean的启动方式dubbo还提供了spring的注解及配置文件方式完成以上配置。以配置文件方式为例子:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--定义了提供方应用信息,用于计算依赖关系;在 dubbo-admin 或 dubbo-monitor 会显示这个名字,方便辨识-->
<dubbo:application name="test"/>
<!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!--使用 dubbo 协议实现定义好的 api.PermissionService 接口-->
<dubbo:service interface="dubbo.GreetingsService" ref="greetingsServiceImpl" protocol="dubbo" />
<!--具体实现该接口的 bean-->
<bean id="greetingsServiceImpl" class="dubbo.GreetingsServiceImpl"/>
<dubbo:reference id="greetingsService" interface="dubbo.GreetingsService"/>
</beans>
public class SpringDubboTest {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring-dubbo.xml");
GreetingsService greetingsService = (GreetingsService) context.getBean("greetingsService");
System.out.println(greetingsService.sayHi("oyooo"));
}
}
- spring通过DubboNamespaceHandler类解析"dubbo"标签,分别对应
标签名称 | 类名 |
---|---|
dubbo:application | ApplicationConfig |
dubbo:module | ModuleConfig |
dubbo:registry | RegistryConfig |
dubbo:monitor | MonitorConfig |
dubbo:provider | ProviderConfig |
dubbo:consumer | ConsumerConfig |
dubbo:protocol | ProtocolConfig |
dubbo:service | ServiceBean |
dubbo:reference | ReferenceBean |
- service服务提供者启动流程
- 消费者启动时
ServiceRepository功能及属性
ServiceRepository是存储了所有服务端发布的服务、客户端需要访问的服务,通过ServiceRepository可以获取所有本dubbo实例发布的服务和引用的服务。
简而言之:就是你项目dubbo服务的元数据信息
ServiceRepository是通过ApplicationModel.getServiceRepository方法创建或者获取的。里面包括三个字段:services、consumers、providers。作用分别是:
- services:类型是ConcurrentMap<String, ServiceDescriptor>,key是远程服务的接口名。
- ServiceDescriptor记录了服务接口的Class对象,接口名,服务接口每个方法的名字、入参类型、返回值类型等详细信息,可以理解为记录了远程服务接口的详细描述.
- 客户端、服务端在启动的时候都会调ServiceRepository.registerService方法将ServiceDescriptor对象注册到services中。下面两个属性需要的ServiceDescriptor对象也是从services中获取的。
- consumers:类型是ConcurrentMap<String, ConsumerModel>,key是由服务接口+“:”+group+“:”+version组成的serviceKey.
- ConsumerModel中也有serviceKey,除了serviceKey之外还有ServiceDescriptor、ReferenceConfig对象、MethodConfig配置信息、可以访问远程服务提供者的Invoker对象。
- 在客户端启动的时候,dubbo会调用ReferenceConfig的init方法,在init方法里面调用ServiceRepository.registerConsumer方法,该方法会创建ConsumerModel对象,并将其注册到ServiceRepository中。
- 之后,dubbo可以访问该consumers属性,获取所有的客户端需要访问的远程服务信息。
- providers:类型是ConcurrentMap<String, ProviderModel>,key是由服务接口+“:”+group+“:”+version组成的serviceKey.
- ProviderModel中也有serviceKey,除了serviceKey之外还有ServiceDescriptor、ServiceConfig对象以及对外提供服务的spring bean对象。
- 在服务端启动暴露服务的时候,dubbo会调用ServiceConfig的doExportUrls方法,在doExportUrls方法里面调用ServiceRepository.registerProvider方法,该方法会创建ProviderModel对象,并将其注册到ServiceRepository中。
- 之后,dubbo可以访问该providers属性,获取所有服务端发布的服务信息。
另外dubbo中也支持远程存储这些元数据,具体参考WritableMetadataService类
总结
- 服务启动时,先是服务准备好并且本地爆露完再向注册中心提交连接。
- 防止过早的向注册中心写入,请求找不到服务.
- @see RegistryProtocol.export
- 服务停止时,先是取消注册中心连接,然后等请求完成关闭
- 阻止新请求过来,然后再等待正在运行的请求处理完成
- @see RegistryProtocol.ExporterChangeableWrapper.unexport
主要参考
《源码分析Dubbo前置篇-寻找注册中心、服务提供者、服务消费者功能入口》
《源码分析Dubbo服务消费端启动流程》
《源码分析Dubbo服务提供者启动流程》