原文链接:https://blog.csdn.net/weixin_40533111/article/details/83660571 作者四月天五月雨^_^,转载请注明出处,谢谢
声明
本文参考dubbo官网:http://dubbo.apache.org/en-us/docs/user/preface/architecture.html
基础架构,理论篇可参考:dubbo使用小全 分析 理解 附GitHub 源码 ( 一 )
简单搭建demo可参考:dubbo使用小全 分析 理解 附GitHub 源码 ( 二 )
本文在上篇的基础上继续写,展示dubbo的其他功能,都是些配置
dubbo配置有缺省值,优先级:具体服务>全局配置>缺省值
1.启动时检查
dubbo默认在启动时检查服务是否可用,不可用的话,应用启动失败,而有些场景比如:A应用依赖B的一些服务,B应用同时依赖A应用的一些服务,那么按照默认情况,启动谁都是错,需放弃启动时检查,
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setTimeout(3000);
//启动时检查,默认为true,当服务不可用或多个应用存在循环依赖,需关闭检测,以防启动失败,若后面时间中,服务恢复,dubbo的心跳检查会自动连上
consumerConfig.setCheck(false);
return consumerConfig;
}
2.集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,默认为 failover 重试,
1.invoker是provider的一个服务,封装和provider地址和具体接口service的信息
2.Directory是多个Invoker,类似List,Cluster将Directory中的多个Invoker做了统一管理,这样对外暴露的接口统一,调用者感知不到具体的服务提供者
3.Router根据规则可在(集群)服务中找到具体哪一台的机器上的服务进行调用
4.loadBalance负载均衡策略,Router路由就是通过这个策略选出具体的服务提供者,缺省为Failover,失败重试策略(一台调失败,则调另外一台,可配置providerConfig.setRetries(2);设置重试次数)
5.一些其他集群容错算法有:
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息。
/**
* 设置集群容错策略,当失败了怎么滴,怎么滴
*/
providerConfig.setCluster("failsafe");
3.负载均衡
常见的负载均衡算法有:
1.random(默认)
随机(默认),可按权重设置随机概率,
缺点:当调用者少时,在某个点碰撞的机会较大,但调用者多起来后,分布就越来越均匀,数学书上有个投硬币的故事,当基数趋于无穷大,概率就均匀了.
2.RoundRobin
按公约后的权重设置轮循比率
缺点:存在慢的机器积累请求的问题,当某台机器很慢,但没死掉,则请求调到这台,都卡在这了,这台没响应,请求一直都堆积在这
3.LeastActive
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
4.ConsistentHash
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
/**
* 负载均衡策略,默认为random
*/
providerConfig.setLoadbalance("roundrobin");
4.多协议
dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。
可根据服务性质具体配置,比如这个服务使用rmi协议,可传输视频,音频等大文件,对数据准确度要求不高等
@Service(timeout = 3000, cluster = "failfast", protocol = {"dubbo", "rmi"})
public class UserServiceImpl implements UserService {
@Override
public String getUser(Long id) {
return "provider user " + id;
}
}
全局配置不建议
//注册多协议,此处是全剧配置,可在每个具体服务上单独配置,
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
ProtocolConfig protocol2 = new ProtocolConfig();
protocol2.setName("rmi");
protocol2.setPort(2083);
List<ProtocolConfig> protocolConfigList = new ArrayList<>(2);
protocolConfigList.add(protocol);
protocolConfigList.add(protocol2);
providerConfig.setProtocols(protocolConfigList);
5.多注册中心
dubbo支持同一服务注册到多注册中心,也支持不同服务注册到不同注册中心,终极使用:消费端也可以同时消费多注册中心的同包同名服务
注意:
1.同一注册中心(ip,端口)
2.服务版本version
3.分组group
4.同包同名
4者一致,才算是一个可用服务
provider全局配置声明注册中心:
/**
* 对spring声明bean名称,方便使用时注入
* @return
*/
@Bean(name = "hangzhouRegistry")
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.5.6.7:1234");
registryConfig.setId("hangzhou");
registryConfig.setGroup("hangzhou");
return registryConfig;
}
@Bean(name = "shanghaiRegistry")
public RegistryConfig registryConfig2() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.5.6.7:12345");
registryConfig.setId("shanghai");
return registryConfig;
}
**具体服务:**
@Service(timeout = 3000, registry = "hangzhouRegistry", version = "1.0.0", group = "test1")
public class UserServiceImpl implements UserService {
consumer全局配置声明注册中心:
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.5.6.7:1234");
registryConfig.setClient("curator2");
return registryConfig;
}
@Bean
public RegistryConfig registryConfig2() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.5.6.7:12345");
registryConfig.setClient("curator2");
return registryConfig;
}
**具体服务:**
@Controller
public class User {
@Reference(registry = "hangzhouRegistry", group = "test1", version = "1.0.0")
// @Reference(registry = "shanghaiRegistry", group = "test12", version = "1.0.0")
private UserService userService;
6.服务分组
注意:
1.同一注册中心(ip,端口)
2.服务版本version
3.分组group
4.同包同名
4者一致,才算是一个可用服务
按功能对服务分组,配置参考第五条: 5.多注册中心
7.多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
1.在低压力时间段,先升级一半提供者为新版本
2.再将所有消费者升级为新版本
3.然后将剩下的一半提供者升级为新版本
版本使用
配置参考第五条: 5.多注册中心
8.异步调用
当有些耗时操作,可采用异步调用,使用dubbo内置的异步调用,比自己开线程更节省资源
异步分两种,需要返回结果的,可不需要结果的
首先provider
@Service(timeout = 30000, registry = "hangzhouRegistry", version = "1.0.0", group = "test1")
public class UserServiceImpl implements UserService {
@Override
public String getUser(Long id) {
Preconditions.checkArgument(id != null);
try {
//模仿耗时,睡几秒
if (id > 100) {
TimeUnit.SECONDS.sleep(3);
} else {
TimeUnit.SECONDS.sleep(6);
}
return "provider user " + id;
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
注意: 因为耗时操作:需要把超时时间预留的大些
consumer
@Reference(registry = "hangzhouRegistry", group = "test1", version = "1.0.0", async = true,timeout = 30000)
private UserService userService;
@RequestMapping(value = "getNameByAsync", method = {RequestMethod.GET}, produces = "application/json")
@ResponseBody
public String getNameByAsync() {
String result = null;
long beginTime = Instant.now().getEpochSecond();
if (userService != null) {
//此调用会立刻返回null
userService.getUser(12L);
System.out.println("send成功------");
long sendTime = Instant.now().getEpochSecond();
System.out.println("send耗时:" + (sendTime - beginTime));
System.out.println("因为为dubbo为异步调用,并未等待,继续跑,此处可执行其他业务代码~~~~");
try {
//拿到调用者的future引用,当有结果了,会通知并设置到此future
Future<String> future = RpcContext.getContext().getFuture();
//当没有结果返回时,会wait在这,直到有结果,所以如果业务不需要返回值,这一步不用即可
//客户端不需要启动多线程来支持并行,而是借助NIO的非阻塞完成
String user = future.get();
result = "异步调用成功------" + user;
long waitTime = Instant.now().getEpochSecond();
System.out.println("异步等待耗时:" + (waitTime - sendTime));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} else {
System.out.println("调用失败------");
result = "调用失败------";
}
long endTime = Instant.now().getEpochSecond();
System.out.println("总耗时为:" + (endTime - beginTime));
return result;
}
和手动多线程类似,dubbo使用nio非阻塞完成,当调用多个异步服务时,效率更高.
还有很多其他的配置,可参考官网
未完待续--------