为什么需要服务治理
- 过多的服务 URL 配置困难
- 负载均衡分配节点压力过大的情况下也需要部署集群
- 服务依赖混乱,启动顺序不清晰
- 过多服务导致性能指标分析难度较大,需要监控
1、Dubbo体系结构
2、Dubbo 容错机制
Dubbo 集群容错架构图
各节点关系:
- 这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息。
- Directory 代表多个 Invoker,可以把它看成 List,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更。
- Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
- Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等。
- LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
2.1 集群容错模式
Failover Cluster
- 失败自动切换,当出现失败,重试其它服务器。(缺省)
- 通常用于读操作,但重试会带来更长延迟。
- 可通过 retries=”2” 来设置重试次数 (不含第一次)。
Failfast Cluster
- 快速失败,只发起一次调用,失败立即报错。
- 通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
- 失败安全,出现异常时,直接忽略。
- 通常用于写入审计日志等操作。
Failback Cluster
- 失败自动恢复,后台记录失败请求,定时重发。
- 通常用于消息通知操作。
Forking Cluster
- 并行调用多个服务器,只要一个成功即返回。
- 通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
- 可通过 forks=”2” 来设置最大并行数。
Broadcast Cluster
- 广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0 开始支持)
- 通常用于通知所有提供者更新缓存或日志等本地资源信息。
<dubbo:service retries="2" />
<dubbo:reference retries="2" />
<!-- 单一方法配置重试次数 -->
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
<!-- 配置集群容错模式 -->
<dubbo:service cluster="failsafe" />
<dubbo:reference cluster="failsafe" />
3、Dubbo 服务降级
综述:
Dubbo 服务降级主要包含两点:屏蔽(mock=force,Dubbo-admin 配置)和容错(mock=fail,Mock)。
使用 Dubbo 时,可能会遇到以下问题:
1)多个服务之间可能由于服务没有启动或者网络不通,调用中会出现远程调用失败;
- 服务请求过大,需要停止部分服务以保证核心业务的正常运行;
mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
以上两个问题可以使用 Dubbo 的服务降级来实现;
即:在服务宕掉或者并发数太高导致的 RpcException 异常时,进行友好的处理或者提示,而不是内部报错导致系统不可用。
查看 dubbo 的官方文档,可以发现有个 mock 的配置,mock 只在出现非业务异常 (比如超时,网络异常等) 时执行。mock 的配置支持两种,一种为 boolean 值,默认的为 false。如果配置为 true,则缺省使用 mock 类名,即类名 + Mock 后缀;另外一种则是配置返回默认字符串,如:”return null” 或者 “return 123456”,可以很简单的忽略掉异常。
/**接口定义*/
public interface IUser {
public void addUser(User u);
public User getUserById(int id);
}
/**实现类*/
public class UserImpl implements IUser {
private static List<User> USER_LIST = new ArrayList<User>();
static{
for(int i=0;i<10;i++){
User u = new User();
u.setAddress("address"+i);
u.setId(i);
u.setName("name"+i);
USER_LIST.add(u);
}
}
public void addUser(User u) {
USER_LIST.add(u);
System.out.println("total:"+USER_LIST.size());
}
public User getUserById(int id) {
for(int i=0;i<USER_LIST.size();i++){
if(USER_LIST.get(i).getId() == id){
return USER_LIST.get(i);
}
}
return null;
}
}
public class IUserMock implements IUser {
@Override
public void addUser(User u) {
throw new RuntimeException("add user fail!");
}
@Override
public User getUserById(int id) {
return null;
}
}
<dubbo:reference id="iUser" interface="com.dubbosample.iface.IUser" timeout="10000" check="false" mock="true">
<dubbo:reference id="iUser" interface="com.dubbosample.iface.IUser" timeout="10000" check="false" mock="return null">
以上配置还可以在 dubbo-admin 界面中配置
分别是屏蔽和容错:
其中屏蔽只能在 dubbo-admin 中配置,不能在 dubbo 配置文件里配置
屏蔽:force.mock (即:屏蔽请求,直接返回某个值,如上面的字符串,mock=“return 123456”);
容错:fail.mock (即:允许请求,在请求失败的时候,再返回某个值,如:mock=“fail:return 123456”);
4、Dubbo 服务限流
Dubbo 限流主要依赖 Sentinel(哨兵)中间件一起使用
服务提供端限流:
对服务提供方的限流可分为服务提供方的自我保护能力和服务提供方对服务消费方的请求分配能力这两个维度。
QPS & 定向限流
服务消费端限流:
对服务提供方的限流可分为对控制并发线程数,和服务降级两个维度。
详见:
https://yq.aliyun.com/articles/624053?utm_content=m_1000013383
拓展:
https://blog.csdn.net/world_snow/article/details/79080314
5、Dubbo 负载均衡
dubbo 有四种负载均衡的方式:
1)RandomLoadBalance:加权随机算法(默认)
2)LeastActiveLoadBalance:最小活跃负载均衡
3)ConsistentHashLoadBalance:一致性 hash 负载均衡
4)RoundRobinLoadBalance:加权轮询负载均衡
6、Dubbo 结果缓存
https://dubbo.apache.org/zh-cn/docs/user/quick-start.html