深度学习 Dubbo
CSDN博客专家:https://summer.blog.csdn.net/
GitHub开源博主:https://github.com/Jaysong2012 欢迎大家围观。
Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC
框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Apache 如是评价这个由阿里巴巴捐赠给 Apache 并成为 Apache 顶级项目的 Java 高性能 RPC 服务框架。
如果到目前为止,你还不知道或者不了解 Dubbo ,请移步百度百科或者查看 Dubbo 官网 http://dubbo.apache.org ,Dubbo
的介绍说明,不是本课题的重点。
Java RMI 入门
Java RMI (Remote Method Invocation)- 远程方法调用。它的核心思想就是让你的客户端像使用本地调用一样调用服务端 Java
虚拟机中的对象方法。RMI 是面向对象语言领域对 RPC (Remote Procedure Call)的完善,它让我们无需依靠 IDL
的帮助来完成分布式调用,而通过依赖接口这种更简单自然的方式。
Java RMI 是 Java 领域创建分布式应用的技术基石。后续的 EJB 技术,以及现代的分布式服务框架,其中的基本理念依旧是 Java RMI
的延续。
一个典型的 RMI 调用将有以下几个角色组成:
- *注册服务(registry) *: 提供注册表的创建以及查找和命名远程对象的类、接口和异常;
- 客户端(Client) : 远程服务调用方;
- 服务端(Server) : 远程服务提供方;
- 本地存根对象(Stub) : 服务端远程对象的代理对象 stub;
- 服务端的骨架对象(Skeleton) : 服务端远程对象的辅助对象 skeleton。
一个典型的 RMI 调用将会经历如下几个步骤:
- 服务端向 RMI 注册服务绑定自己的地址;
- 客户端通过 RMI 注册服务获取目标地址;
- 客户端调用本地的 Stub 对象上的方法,和调用本地对象上的方法一致;
- 本地存根对象将调用信息打包,通过网络发送到服务端;
- 服务端的 Skeleton 对象收到网络请求之后,将调用信息解包;
- 然后找到真正的服务对象发起调用,并将返回结果打包通过网络发送回客户端。
完整流程如下图所示:  图片来源:https://www.cs.rutgers.edu/~pxk/417/notes/images/rpc-
rmi_flow.png
下面我们通过示例代码来看看 RMI 中的服务注册和发现。
定义一个远程服务接口
public interface Service extends Remote { void sayHello(String name) throws RemoteException;
} `
远程服务接口实现
public class ServiceImpl implements Service{ @Override
public void sayHello(String name) {
System.out.println("hello "+name);
}
} `
服务端的服务注册
public static void main(String[] args) throws RemoteException{ Service service = new ServiceImpl(); // 初始化服务对象实例 // 通过 UnicastRemoteObject.exportObject 生成可以与服务端通讯的 Stub 对象 Service stub = (Service) UnicastRemoteObject.exportObject(service, 19898); //创建一个本地的 RMI 注册服务,监听端口为 1099。该注册服务运行在服务端,也可以单独启动一个注册服务的进程 Registry registry = LocateRegistry.createRegistry(1099); //将 Stub 对象绑定到注册服务上,这样,客户端可以通过 service 这个名字查找到该远程对象。 registry.rebind("service", stub); // #4 }
客户端的服务发现
public static void main(String[] args) throws RemoteException, NotBoundException { //在 1099 端口上获取注册服务实例 Registry registry = LocateRegistry.getRegistry(1099); 从注册服务中查找服务名为 Service 的远程对象 Service stub = (Service) registry.lookup("service"); //通过获取的 Stub 对象发起一次 RMI 调用 stub.sayHello("jaysong");
}
分别先后启动服务端客户端进程,我们将在客户端成功调用远程服务端提供的远程方法。
Dubbo 基础
Dubbo 的分布式服务框架的基本概念与 RMI 是类似的,同样是使用 Java 的 Interface
作为服务契约,通过注册中心来完成服务的注册和发现,远程通讯的细节也是通过代理类来屏蔽。具体来说,Dubbo 在工作时有以下四个角色参与:
角色 | 说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调用关系如下:
- 服务容器负责启动,加载,运行服务提供者;
- 服务提供者在启动时,向注册中心注册自己提供的服务;
- 服务消费者在启动时,向注册中心订阅自己所需的服务;
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用;
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
具体系统架构图如下:
图片来源:http://dubbo.apache.org/docs/zh-cn/user/sources/images/dubbo-
architecture.jpg
Dubbo实战
Dubbo 应用一般依托于 Spring 容器(也可以是其他容器),如果我们希望深度学习
Dubbo,自己动手实践是必要的,为了降低大家的学习成本,我这边将以 SpringBoot 为例,Dubbo 的 SpringBoot 集成方案欢迎访问
https://github.com/apache/incubator-dubbo-spring-boot-project
,上面有详细的构建说明,我这里将不赘述了。如果实在不会自己构建,欢迎使用我的 Dubbo
案例项目dubbo(传送门),上面有你想要的全部高级案例。
Dubbo 集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
 图片来源: http://dubbo.apache.org/docs/zh-
cn/user/sources/images/cluster.jpg
Failover Cluster 失败自动切换,当出现失败,重试其它服务器 。通常用于读操作,但重试会带来更长延迟。可通过 retries=”2” 来设置重试次数(不含第一次)。
@Service(version = "1.0.0",protocol = {"dubbo"},registry = {"zk","redis"},retries = 3,owner = "jaysong")
Failfast Cluster 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
@Service(version = "1.0.0",protocol = {"dubbo"},registry = {"zk"},cluster = "failfast",owner = "jaysong")
Failsafe Cluster 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2” 来设置最大并行数。
Broadcast Cluster 广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡负载均衡策略,缺省为 random 随机调用。
Random LoadBalance 随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
@Service(version = "1.0.0",protocol = {"dubbo"},registry = {"zk","redis"},retries = 3,owner = "jaysong",loadbalance = "roundrobin")
//配置每一个method的信息
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("errorTest");
methodConfig.setTimeout(1000);
methodConfig.setLoadbalance("random");
methodConfig.setRetries(1);
RoundRobin LoadBalance 轮询,按公约后的权重设置轮询比率。 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
//配置每一个method的信息
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName(“errorTest”);
methodConfig.setTimeout(1000);
methodConfig.setLoadbalance(“roundrobin”);
methodConfig.setRetries(1);LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance 一致性 Hash,相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
Dubbo 多协议
Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。
- dubbo:// Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
 图片来源
http://dubbo.apache.org/docs/zh-cn/user/sources/images/dubbo-protocol.jpg
rmi:// RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。适用于传入传出参数数据包大小混合,消费者与提供者个数差不多,可用于传文件。
hessian:// Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。适用于传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可用于传文件。 Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:
- 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
- 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。
Dubbo 其他可用的协议包括 http://
、webservice://、thrift://、memcached://、redis://、rest://。如果有兴趣了解,请移步
http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html
这里不在赘述。
我们是实际使用中,考虑到性能的需要经常需要不同的服务使用不同的协议,当然也有可能不同服务不同注册中心,这里我给大家做了详细演示。
# Dubbo Application
## The default value of dubbo.application.name is ${spring.application.name}
dubbo:
application:
name: dubbo-provider
scan:
base-packages: com.example.dubbo.provider.serviceImpl
protocols:
rmi:
name: rmi
port: 1088
serialization: hessian2
dubbo:
name: dubbo
port: 20880
server: netty
client: netty
codec: dubbo
serialization: hessian2
charset: UTF-8
threadpool: fixed
threads: 100
queues: 0
iothreads: 9
buffer: 8192
accepts: 1000
payload: 1048576
hessian:
name: hessian
port: 18899
serialization: hessian2
payload: 10485760
charset: UTF-8
server: jetty
registries:
zk:
address: zookeeper://192.168.199.8:2181
default: true
redis:
address: redis://192.168.199.7:6379
如上的配置中,我们配置了 3 种不同的协议和 2 个注册中心应对不同的场景需要。
对应服务使用对应协议的配置如下:
@Service(version = “1.0.0”,protocol = {“dubbo”},registry = {“zk”,”redis”},retries = 3,owner = “jaysong”,loadbalance = “roundrobin”)
@Service(version = “1.0.0”,protocol = {“hessian”},registry = {“zk”})
Dubbo Admin控制台
Dubbo 在阿里巴巴时代的时候有一个 dubbo-admin 的监控管理工具,Apache 时代在 Dubbo 2.6.0 版本以前都用的是 dubbo-
admin,可以前往 GitHub 上下载 dubbo-admin:https://github.com/apache/incubator-
dubbo/tree/dubbo-2.6.0 ,或者直接下载一个 dubbo-admin.war 包,修改配置 dubbo.properties。
# 保持心跳的注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 用户名
dubbo.admin.root.password=root
# 密码
dubbo.admin.guest.password=guest
然后直接在 Tomcat 等 Web 容器中运行,运行效果如下:
Dubbo 2.6.0 版本以后,Apache 升级了dubbo-amdin 提供了 incubator-dubbo-ops 管理控制台运维工具使用。
当然很多功能都还在不断的版本迭代升级中(需要优化的点还很多)。可以到 https://github.com/apache/incubator-dubbo-
ops
这里下载最新的,当然我也集成到我的项目里了([传送门](https://github.com/Jaysong2012/dubbo/tree/master/dubbo-
admin))。
运行步骤如下:
修改 application.properties 中的心跳注册中心地址为你自己使用的注册中心地址;
admin.registry.address=zookeeper://127.0.0.1:2181
admin.config-center=zookeeper://127.0.0.1:2181
admin.metadata.address=zookeeper://127.0.0.1:2181运行 dubbo-admin-backend 项目(spring boot 工程),默认使用 8080 端口,如果端口冲突,可以自行修改;
启动 dubbo-admin-frontend 项目(Node.js Vue 工程,需要安装Node.js),启动步骤如下:
# 安装依赖
npm install
启动项目,默认运行在 8081 端口
npm run dev
此时访问 http://localhost:8081 ,你将看到如下管理控制台运维页面。
例如,我们查看一个服务的明细: 
其他,它可以提供的功能包括:
这里我就不一一演示了,都很简单,大家自己摸索。
本文首发于 GitChat,未经授权不得转载,转载需与 GitChat 联系。
12
写评论