作者:幻好
在分布式服务架构的背景下,为了服务间高效的通信,常常使用 RPC 进行解决,提到 RPC 就不得不学习 Dubbo
设计背景
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,需要一个治理系统确保架构有条不紊的演进。
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心( SOA )是关键。在此背景下,Dubbo 应运而生。
RPC
在介绍 Dubbo 之前,我们得先了解什么是 RPC?
RPC,Remote Procedure Call 即远程过程调用,远程过程调用其实对标的是本地过程调用。当项目中服务开始增加时,实现服务之间的相互通信。
而 Dubbo 的设计就是为了实现面向接口代理的高性能RPC调用,提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。
初识 Dubbo
首先我们先来了解下 Dubbo 的架构图:
节点 | 角色说明 |
---|---|
Provider | 服务提供方 |
Consumer | 需要调用远程服务的服务消费方 |
Registry | 注册中心 |
Container | 服务运行的容器 |
Monitor | 监控中心 |
Dubbo 整体的运行流程:
- 首先服务提供者Provider 启动,然后向注册中心注册所能提供的服务。
- 服务消费者Consumer 启动,向注册中心订阅自己所需的服务,并获取服务提供者的地址。
- 注册中心将提供者元数据通知给 Consumer,通过负载均衡选择一个 Provider 直接调用 。
- 服务提供方元数据发生变更时,注册中心将基于长连接推送变更数据给服务消费者。
- 服务提供者和消费者都会在内存中记录着调用的次数和时间,定时的发送统计数据到监控中心。
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。
连通性
- 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
- 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
- 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
- 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
- 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
- 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
- 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
健壮性
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
伸缩性
- 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
- 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
升级性
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。
实践
使用 Maven 引入 Dubbo
首先搭建SpringBoot + Zookeeper + Dubbo 项目,引入相关依赖。
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
定义服务接口
定义一个服务接口 ProvideService
,该接口可以处理我们相关业务。
@Service
public class ProvideServiceImpl implements ProvideService {
@Override
public String provideHello(String say) {
System.out.println("Provider say: " + say);
return say;
}
}
配置服务
通过传统的 Spring XML 的方式来装配并暴露 Dubbo 服务,方便项目的配置管理,也可使用注解的方式进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<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-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="provide"/>
<!-- 监控中心的配置 -->
<dubbo:monitor protocol="registry"/>
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="N/A"/>
<!-- 用dubbo协议在20881端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20881"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.demodubbo.provider.service.ProvideService"
ref="provideService"/>
<!-- 本地bean实现服务 -->
<bean id="provideService" class="com.demodubbo.provider.service.impl.ProvideServiceImpl"/>
</beans>
服务提供方的启动类
通过注解的方式,加载服务的配置文件。
@ComponentScan(value = {"com.demodubbo.provider"})
@ImportResource(value = "provide.xml")
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
启动服务提供方
启动项目后,可以在控制台查看暴露的服务信息。
[DUBBO] The service ready on spring started. service: com.demodubbo.provider.service.ProvideService, dubbo version: 2.6.6, current host: 192.168.199.178
[DUBBO] Export dubbo service com.demodubbo.provider.service.ProvideService to local registry, dubbo version: 2.6.6, current host: 192.168.199.178
[DUBBO] Export dubbo service com.demodubbo.provider.service.ProvideService to url dubbo://192.168.199.178:20881/com.demodubbo.provider.service.ProvideService?anyhost=true&application=provide&bean.name=com.demodubbo.provider.service.ProvideService&bind.ip=192.168.199.178&bind.port=20881&dubbo=2.0.2&generic=false&interface=com.demodubbo.provider.service.ProvideService&methods=provideHello&pid=1784&side=provider×tamp=1626011040996, dubbo version: 2.6.6, current host: 192.168.199.178
[DUBBO] Start NettyServer bind /0.0.0.0:20881, export /192.168.199.178:20881, dubbo version: 2.6.6, current host: 192.168.199.178
发起调用
@RestController
public class ConsumerController {
@Autowired
private ProvideService providerService;
@GetMapping("/hello")
public void sayHello(){
String str = providerService.provideHello("Not hello, just hi. ");
System.out.println("consumer get str = " + str + " from provide");
}
}