分布式服务之Dubbo
一、Dubbo介绍
Dubbo是一款高性能、轻量级的开源 Java 服务框架。
提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。
Dubbo的主要功能
- RPC调用
- 支持多协议(序列化、传输)
- 服务注册发现
- 配置、元数据管理
- 集群、高可用、管控
- 集群、负载均衡
- 治理、路由
- 控制台、管理与监控
二、Dubbo技术原理
整体架构图:
- Config配置层:对外配置接口,以ServiceConfig,ReferenceConfig为中心,可以直接初始化配置类,也可以通过Spring解析配置生成配置类
- Proxy 服务代理层:服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory
- Registry 注册中心层:封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory,Registry,RegistryService
- Cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster,Directory,Router,LoadBalace
- Monitor 监控层:RPC调用次数和调用时间的监控,以Statistics为中心,扩展接口为MonitorFactory,Monitor,MonitorService
- Protocol 远程调用层:封装RPC调用,以Invocation,Result为中心,扩展接口为Protocol,Invoker,Exporter
- Exchange 信息交换层:封装请求响应模式,同步转异步,以Request,Response为中心,扩展接口为Exchanger,ExchangeChannel,ExchangeClient,ExchangeServer
- Transport 网络传输层:抽象mina和netty为统一接口,以Message为中心,扩展接口为Channel,Transporter,Client,Server,Codec
- Serialize 数据序列化层:可复用的一些工具,扩展接口为Serialization,ObjectInput,ObjectOutput,ThreadPool
SPI的应用
SPI与API
ServiceLoader机制
META-INF/dubbo/接口全限定名,文件内容为实现类
其他两个类似的机制:Callback和EventBus
服务如何暴露
服务如何引用
集群与路由
- Router :选取此次调用可以提供服务的Invoker集合
- LoadBalance :从上述集合中选取一个作为最终调用者
泛化引用
GenericService
当我们知道接口、方法和参数时,可以使用反射调用服务。
方法1:
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" /> GenericService barService = (GenericService) applicationContext.getBean("barService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
方法2:
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setInterface("com.xxx.XxxService");
reference.setVersion("1.0.0");
reference.setGeneric(true);
GenericService genericService = reference.get();
隐式传参
Context模式
RpcContext.getContext().setAttachment("index","1");
此参数可以传播到RPC调用的整个过程
Mock
将mock设为true,在实现一个对应的Mock实现类即可。如HelloService的Mock类为HelloServiceMock
三、Dubbo最佳实践
开发分包
-
建议将服务接口、服务模型、服务异常等均放在API包中,因为服务模型和异常也是API的一部分,这样做也服务分包原则。
-
服务接口尽可能大粒度,每个服务方法代表一个功能,否则将面临分布式事务问题
-
服务接口建议以业务场景为单位划分
-
不建议使用过于抽象的通用接口,如Map query(Map map),没有明确的语义,会给后期维护带来不便
环境隔离与分组
如何做到多环境隔离呢?
- 隔离性的部署多套
- 多注册中心机制
- Dubbo提供的group机制
- Dubbo提供的版本机制
参数配置
通用参数以consumer端为准,如果consumer端没有设置,则用provider数值
建议在Provider端配置的Consumer端属性有:
- timeout: 方法调用超时时间
- retries:失败重试次数
- loadbalance:负载均衡算法
- actives:消费者端的最大并发调用限制。
建议在Provider端配置的Provider端属性有:
- threads:服务线程池大小
- executes:一个服务提供者并行执行请求上限。
容器化部署
注册的ip问题,解决方法:
- 方法1: docker使用宿主机网络
- docker参数指定注册的ip和端口
运维和监控
Admin里面功能比较简单,可以整合自己公司的监控系统:
- Promethus + Grafana
重试和幂等
服务调用失败会默认重试,所以接口需要幂等性
设计幂等接口方法:
- 去重。 可以通过redis、bitMap等
- 类似乐观锁机制判断
四、源码解析
带着问题去看
- 如何代理服务的?
- 如何发送远程请求的?
- 配置如何生效的?
- 异常处理?
- 超时机制?
- 优雅停机?