Dubbo进阶
注册中心zookeeper
什么是注册中心:
注册中心就是用来存储服务信息的地方,就像房屋中介一样;
为什么需要注册中心:
在前面的例子中我们使用了客户端与服务器直连的方式完成了服务的调用,在实际开发中这回带来一些问题,例如服务器地址变更了,或服务搭建了集群,客户端不知道服务的地址,此时注册中心就派上用场了,服务提供方发布服务后将服务信息放在zookeeper中,然后消费方从zookeeper获取服务器信息,进行调用,这样就使提供方和消费方解开了耦合,也让服务提供方可以更方便的搭建集群;
使用:
1.启动zookeeper,单机和集群都可以
2.在双方配置文件中指定注册中心的信息(内容相同)
<!--注册中心 N/A 表示不使用注册中心 直连客户端 地址可以是一个或多个 多个表示集群-->
<dubbo:registry protocol="zookeeper" address="10.211.55.6:2181,10.211.55.7:2181"/>
需要说明的是,注册中心不是必须使用zookeeper,dubbo还支持其他三种:Simple,Redis,Multicast,因其优秀的可用性,官方推荐使用zookeeper;
Dubbo的其他配置方式
API配置
简单的说就是不使用配置文件而是使用使用代码来完成配置,该方式主要用于测试环境或集成其他框架,不推荐用于生产环境;
服务提供者:
public class ProviderApplication {
public static void main(String[] args) throws IOException {
// xmlConfig();
apiConfig();
System.in.read();//阻塞主线程保持运行
}
private static void apiConfig() {
//应用配置
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("my-service");
applicationConfig.setQosEnable(true);
//注册中心
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181");
//rpc协议
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
//发布服务
ServiceConfig<HelloService> serviceConfig = new ServiceConfig<HelloService>();
serviceConfig.setApplication(applicationConfig);
serviceConfig.setProtocol(protocolConfig);
serviceConfig.setRegistry(registryConfig);
serviceConfig.setInterface(HelloService.class);
serviceConfig.setRef(new HelloServiceImpl());
serviceConfig.export();
}
消费者:
public class ConsumerApplication {
public static void main(String[] args) {
// xmlConfig();
apiConfig();
}
private static void apiConfig() {
//应用配置
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("my-consumer");
applicationConfig.setQosEnable(false);
//注册中心
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181");
//调用服务
ReferenceConfig<HelloService> serviceReferenceConfig = new ReferenceConfig<HelloService>();
serviceReferenceConfig.setApplication(applicationConfig);
serviceReferenceConfig.setRegistry(registryConfig);
serviceReferenceConfig.setInterface(HelloService.class);
HelloService service = serviceReferenceConfig.get();
String tom = service.sayHello("tom");
System.out.println(tom);
}
注解配置
注解配置是使用较多的一种方式,可加快开发速度,让我们从繁琐的配置文件中解脱出来;
Dubbo使用了Spring容器来管理bean,所以配置方式也大同小异,可使用Configuration将一个类作为配置类;在该类中提供必要的几个bean
服务提供者
配置类:
@Configuration
@EnableDubbo(scanBasePackages = "com.yyh.service")
public class ProviderConfiguration {
//无论如何配置我们最终需要的还是那几个bean
@Bean
public ApplicationConfig applicationConfig(){
//应用配置
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("my-service");
applicationConfig.setQosEnable(true);
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181");
return registryConfig;
}
@Bean
public ProtocolConfig protocolConfig(){
//rpc协议
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
return protocolConfig;
}
}
服务实现类:
//注意该注解是Dubbo提供的 不要用错
@Service
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello: "+name;
}
}
发布服务:
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();//阻塞主线程保持运行
}
消费者
配置类:
@Configuration
@EnableDubbo
@ComponentScan("com.yyh.consumer")
public class ConsumerConfiguration {
@Bean
public ApplicationConfig applicationConfig(){
//应用配置
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("my-consumer");
applicationConfig.setQosEnable(true);
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181");
return registryConfig;
}
}
消费者类:
@Component
public class SayHelloConsumer {
@Reference
private HelloService helloService;
public void sayHello(){
String jack = helloService.sayHello("jack");
System.out.println(jack);
}
}
执行测试:
public class ConsumerApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
SayHelloConsumer consumer = context.getBean(SayHelloConsumer.class);
consumer.sayHello();
}
可以发现消费者不需要指定ProtocolConfig,主要服务端固定端口即可;
使用properties配置
相比xml和api的方式,properties是体量是最轻的,在面对一些简单配置时可以采用properties
服务提供者
在resource下提供名为dubbo.properties的文件,内容如下:
dubbo.application.name=my-service
dubbo.application.owner=jerry
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.registry.address=zookeeper://10.211.55.5:2181
配置类:
@Configuration
@EnableDubbo(scanBasePackages = "com.yyh.service")
@PropertySource("classpath:/dubbo.properties")
public class AnnotationAndPropperties {
}
测试代码:
public class ProviderApplication {
public static void main(String[] args) throws IOException {
annotationAndPropConfig();
System.out.println("服务已启动 按任意键退出");
System.in.read();//阻塞 主线程 保持运行
}
private static void annotationAndPropConfig() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationAndPropperties.class);
context.start();
}
}
消费者
同样提供properties文件,但不需要指定protocol
dubbo.application.name=my-service
dubbo.application.qos.enable=false
dubbo.application.owner=jerry
dubbo.registry.address=zookeeper://10.211.55.6:2181
配置类:
@EnableDubbo
@Configuration
@ComponentScan("com.yyh.consumer")
@PropertySource("classpath:/dubbo.properties")
public class AnnotationAndPropConfiguration {
}
消费者类:
@Component
public class SayHelloConsumer {
@Reference
private HelloService helloService;
public void sayHello(){
String jack = helloService.sayHello("jack");
System.out.println(jack);
}
}
测试类:
public class ConsumerApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
SayHelloConsumer consumer = context.getBean(SayHelloConsumer.class);
consumer.sayHello();
}
强调:注解使用时,扫描服务的实现类使用dubbo提供的EnableDubbo注解,而扫描其他bean用的是spring的ComponentScan注解;
常用配置项
1.启动时检查
默认情况下,dubbo在获取一个服务代理对象时会自动检查依赖(作为消费者)的服务是否可用,若服务不可用则抛出异常阻止容器正常初始化,但在一些情况下我们会希望先启动程序,因为服务可能会在之后的时间里变为可用的;
启动检查注册中心
<!--订阅或或发布时是否检查注册中心的可用性 反复测试没啥用,,,,无论true还是false 在注册中心链接失败时会在后台循环重连-->
<dubbo:registry protocol="zookeeper" address="10.211.55.8:2181,10.211.55.7:2181,10.211.55.6:2181"/>
检查服务提供方(对所有提供者)
<!--这里的指的是从容器中获取一个服务方的代理对象时 即getBean()时是否检查 默认情况下容器是懒加载的,不获取Bean就不会创建Bean-->
<dubbo:consumer check="false"/>
检查服务提供方(对某个提供者)
<!--即getBean()时是否检查 若为false则直接返回一个代理对象-->
<dubbo:reference interface="com.yyh.service.HelloService" id="helloService" check="false"/>
<!--若需要立即初始化代理对象,可指定init为true-->
<dubbo:reference interface="com.yyh.service.HelloService" id="helloService" check="false" init="true"/>
properties文件写法:
dubbo.registry.check=false
#强制修改所有reference的check
dubbo.reference.check=false
#当reference的check为空时有效
dubbo.consumer.check=false
dubbo.reference.com.foo.BarService.check=false
对于消费方,当需要从容器获取一个代理对象时,若注册中心不可用默认循环尝试链接,
若注册中心可用,则判断代理对象的check,默认为true,表示需要的服务不可用则抛出异常,若为false则直接返回代理对象;
服务提供方总是在发布服务前尝试链接注册中心,若注册中心链接无法提供服务则循环尝试直到链接成功为止;
2.集群容错
在后续的使用中我们可能会对某一个服务部署多个示例形成集群,随着项目的运行时间越来越常,一些服务几点可能会宕机或是由于网络原因暂时不可用,集群容错可指定在调用服务失败时dubbo要采取的行为;
dubbo提供以下6种容错机制:
| 策略名称 | 优点 | 缺点 | 主要应用场景 |
|---|---|---|---|
| failover(默认) | 对调用者屏蔽调用失败的信息 | 额外资源开销,资源浪费 | 通讯环境良好,并发不高的场景 |
| failfast | 业务快速感知失败状态进行自主决策 | 产生较多报错的信息 | 非幂等性操作,需要快速感知失败的场景 |
| failsafe | 即使失败了也不会影响核心流程 | 对于失败的信息不敏感,需要额外的监控 | 旁路系统,失败不影响核心流程正确性的场景 |
| failback | 失败自动异步重试 | 重试任务可能堆积 | 对于实时性要求不高,且不需要返回值的一些异步操作 |
| forking | 并行发起多个调用,降低失败概率 | 消耗额外的机器资源,需要确保操作幂等性 | 资源充足,且对于失败的容忍度较低,实时性要求高的场景 |
| broadcast | 支持对所有的服务提供者进行操作 | 资源消耗很大 | 通知所有提供者更新缓存或日志等本地资源信息 |
幂等性:指的是每次调用都会产生相同的结果,即不会对数据进行写操作(增删改)
配置方式:
容错配置分为两个粒度:接口级别,方法级别
服务方配置:
服务方配置即将容错配置放在服务提供方,这样一来所有消费方就可以使用统一的容错机制,而不用每个消费方都配一遍;
<!--接口级别:-->
<dubbo:service interface="com.yyh.service.HelloService" ref="helloService" cluster="failover" retries="2"/>
<!--方法级别:-->
<dubbo:service interface="com.yyh.service.HelloService" cluster="failover" ref="helloService">
<dubbo:method name="sayHello" retries="2"/>
</dubbo:service>
消费方配置:
<!--接口级别:-->
<dubbo:service interface="com.yyh.service.HelloService" ref="helloService" cluster="failsafe"/>
<!--方法级别-->
<dubbo:service interface="com.yyh.service.HelloService" cluster="failover" ref="helloService">
<dubbo:method name="sayHello" retries="2"/>
</dubbo:service>
自定义容错策略
如果上述内置的容错策略无法满足你的需求,还可以通过扩展的方式来实现自定义容错策略。
扩展接口
com.alibaba.dubbo.rpc.cluster.Cluster
3.负载均衡
为了提高系统的可用性,能够承受更大的并发量,我们会将压力的服务部署为集群,但是如果每词请求都交给集群中的同一个节点,那这个几点很可能直接就宕了,所以合理的分配任务给集群中的每一台机器也是我们必须考虑的事情,好在dubbo已经提供相应的功能,我们只需简单的配置即可完成负载均衡;
dubbo支持的任务分配方式:
随机random
顾名思义,从Provider列表中选择随机选择一个,但是我们可以为Provider指定权重,权重越大的被选中的几率越高,因此对于性能更好的机器应设置更大的权重,反之则反,如果不指定负载均衡,默认使用随机负载均衡;
轮询roundrobin
即依次调用所有Provider,每个Provider轮流处理请求,当然我们也可以指定权重,Provider收到的请求数量比约等于权重比; 性能差的机器可能会累积一堆请求,最终拖慢整个系统;
基于活跃数leastactive
每个Provider收到一个请求则将活跃数+1,每处理完成一个请求则活跃数-1,新的请求将会交给活跃数最少的Provider; 简单的说性能越好的机器将收到更多的请求,反之则反;
基于hash一致consistenthash
将根据Provider的 ip 或者其他的信息为Provider生成一个 hash,并将这个 hash 投射到 [0, 2^32 - 1] 的圆环上。当有请求时,则使用请求参数计算得出一个hash值。然后查找第一个大于或等于该 hash 值的缓存Provider,并将请求交予该Provider处理。如果当前Provider挂了 ,则在下一次请求时,为缓存项查找另一个大于其 hash 值的Provider即可。 具体算法参考:去官网看看

配置方法:
与容错配置一样,我们可以选择在服务方或是消费方进行设置;
服务方:
<!--接口级别-->
<dubbo:service interface="com.yyh.service.HelloService" loadbalance="roundrobin" />
<!--方法级别-->
<dubbo:service interface="com.yyh.service.HelloService" cluster="failover" ref="helloService">
<dubbo:method name="sayHello" retries="2" loadbalance="roundrobin"/>
</dubbo:service>
消费方:
<!--接口级别-->
<dubbo:reference interface="com.yyh.service.HelloService" id="helloService" loadbalance="roundrobin"/>
<!--方法级别-->
<dubbo:reference interface="com.yyh.service.HelloService" id="helloService">
<dubbo:method name="sayHello" loadbalance="roundrobin"/>
</dubbo:reference>
直连
即跳过注册中新直接找服务提供方,必须提前明确服务提供方的地址,所以该方式一般仅用于开发调试;
<dubbo:registry address="N/A"/>
仅订阅
仅订阅指的是,不发布服务到注册中心,只从注册中心订阅依赖的服务;
使用场景:当我们要开发一个新的Provider,而这个Provider需要依赖其他Provider时,使用,其目的是避免正在开发的服务发布后被消费方调用,因为开发还未完成,可能造成意想不到的结果; 这就用到了仅订阅,再搭配直连即可完成开发调试;
<dubbo:registry protocol="zookeeper" address="10.211.55.8:2181" register="false"/>
仅注册
仅注册指的是,发布自身服务到注册中心,但不从注册中心订阅依赖的服务;
使用场景: 自身需要对外提供服务,但是依赖的某个服务还在开发调试总,不能正常提供访问;
<dubbo:registry protocol="zookeeper" address="10.211.55.8:2181" subscribe="false"/>
多注册中心
对于一些大型系统,为了加快响应速度,可能会在不同地区进行部署,例如阿里云分布在7个不同城市,有的时候可能因为当地系统还未部署完成,但是仍然需要提供访问,这是就需要我们将相同的服务注册到多个不同的注册中心;
反过来,一些时候当前系统依赖的服务可能部署在不同的注册中心中,这就需要同时向多个不同的注册中心订阅服务;
配置方式也非常简单,添加额外registry即可;
案例:
我们在Common模块中创建新的接口com.yyh.service.UserService,同时在Provider中实现该接口,最后发布到注册中心;
发布到多个注册中心
<!--两个注册中心-->
<dubbo:registry id="reg1" protocol="zookeeper" address="10.211.55.8:2181,10.211.55.7:2181,10.211.55.6:2181"/>
<dubbo:registry id="reg2" protocol="zookeeper" address="10.211.55.7:2188"/>
<!--注册到多个注册中心 id用逗号隔开-->
<dubbo:service registry="reg1,reg2" interface="com.yyh.service.HelloService" ref="helloService" cluster="failsafe" loadbalance="random"/>
<!--userService仅注册到id为reg2的注册中心-->
<dubbo:service registry="reg2" interface="com.kkb.service.UserService" ref="userService" cluster="failsafe" loadbalance="random"/>
<!--实现Bean-->
<bean id="helloService" class="com.kkb.service.impl.HelloServiceImpl"/>
<bean id="userService" class="com.kkb.service.impl.UserServiceImpl"/>
从不同注册中心订阅
<!--两个注册中心-->
<dubbo:registry id="reg1" protocol="zookeeper" address="10.211.55.8:2181,10.211.55.7:2181,10.211.55.6:2181"/>
<dubbo:registry id="reg2" protocol="zookeeper" address="10.211.55.7:2188"/>
<!--从两个注册中心分别订阅 -->
<dubbo:reference interface="com.yyh.service.HelloService" id="helloService" cluster="failover" retries="3" registry="reg1"/>
<dubbo:reference interface="com.yyh.service.UserService" id="userService" cluster="failover" retries="3" registry="reg2"/>
<dubbo:consumer check="false"/>
注意在同一台机器上运行多个实例时zookeeper中的netty默认会监听8080,需要修改admin.serverPort配置,否则无法启动
需要源码请私信
Dubbo进阶的更多相关文章
- SpringCloud Alibaba实战(12:引入Dubbo实现RPC调用)
源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 大家好,我是老三,断更了半年,我又滚回来继续写这个系列了,还有人看吗-- 在前面的章 ...
- 阿里分布式开源框架DUBBO 入门+ 进阶+ 项目实战视频教程
史诗级Java/JavaWeb学习资源免费分享 欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回 ...
- Java进阶专题(二十六) 将近2万字的Dubbo原理解析,彻底搞懂dubbo
前言 前面我们研究了RPC的原理,市面上有很多基于RPC思想实现的框架,比如有Dubbo.今天就从Dubbo的SPI机制.服务注册与发现源码及网络通信过程去深入剖析下Dubbo. Dubbo架构 ...
- Java进阶专题(二十七) 将近2万字的Dubbo原理解析,彻底搞懂dubbo (下)
...接上文 服务发现 服务发现流程 整体duubo的服务消费原理 Dubbo 框架做服务消费也分为两大部分 , 第一步通过持有远程服务实例生成Invoker,这个Invoker 在客户端是核心的远程 ...
- java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘
15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; ...
- Java架构师系统培训高并发分布式电商实战activemq,netty,nginx,redis dubbo shiro jvm虚拟机视频教程下载
15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十五)阶段总结
作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 一 每个阶段在结尾时都会有一个阶段总结,在<SSM整合基础篇& ...
- Dubbo和Spring Cloud微服务架构'
微服务架构是互联网很热门的话题,是互联网技术发展的必然结果.它提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.虽然微服务架构没有公认的技术标准和规范或者草案,但业 ...
- 服务化改造实践 | 如何在 Dubbo 中支持 REST
什么是 REST REST 是 Roy Thomas Fielding [[1]](#fn1) 在 2000 年他的博士论文 [[2]](#fn2) “架构风格以及基于网络的软件架构设计” 中提出来的 ...
随机推荐
- OA项目-表结构
############### 新建APP并配置 ############### INSTALLED_APPS = [ ... 'apps.users.apps.UsersConfig', 'a ...
- 代码审计中的SQL注入
0x00 背景 SQL注入是一种常见Web漏洞,所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.本文以代码审计的形式研 ...
- web接口测试中需要注意的点
1.接口返回 数据格式是否和预期一致.例如:要求返回json格式的数据,json数据的key命名是否正确,对应的value是否与数据库一致. 需要转换的数据是否正确转换,例如时间戳是否按正确转换为时间 ...
- 关联规则之Aprior算法
关联规则挖掘在电商.零售.大气物理.生物医学已经有了广泛的应用,本篇文章将介绍一些基本知识和Aprori算法. 啤酒与尿布的故事已经成为了关联规则挖掘的经典案例,还有人专门出了一本书<啤酒与尿布 ...
- AOP 总结
AOP即Aspect oriented Programing, 面向切面编程. 相关术语: 通知(Advice): Advice defineds when to execute what actio ...
- cannot be found on object of type xx.CacheExpressionRootObject
0 环境 系统环境:win10 编辑器:IDEA 1 前言->环境搭建 1-1 pom依赖 <?xml version="1.0" encoding="UTF ...
- Leetcode7_整数反转
题目 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123输出: 321 示例 2: 输入: -123输出: -321 示例 3: 输入: 120输出: ...
- 机器学习算法之——KNN、Kmeans
一.Kmeans算法 kmeans算法又名k均值算法.其算法思想大致为:先从样本集中随机选取 kk 个样本作为簇中心,并计算所有样本与这 kk 个“簇中心”的距离,对于每一个样本,将其划分到与其距离最 ...
- CAD安装错误1625:系统策略禁止这个安装,请与系统管理员联系。
在安装Autodesk CAD/3DMAX/Maya/Revit/Inventor等的时候,出现“安装错误1625:系统策略禁止这个安装,请与系统管理员联系.”,或是Error 1625,同时还会提示 ...
- python学习笔记(5)数据类型-字典
字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式如下所示: d ...