Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。
基于接口的远程调用、容错、和负载均衡。
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
dubbo:application
: 指定应用程序名称
dubbo:registry
: 指定连接注册中心信息(配置注册中心)
dubbo:protocol
: 服务提供方注册服务采用的协议
dubbo:service
: 对外暴露服务配置
dubbo:reference
: 配置订阅服务配置
导入dubbo-..-starter,在application.properties当中配置属性,使用@DubboService
注解【暴露服务】,使用@DubboReference
注解【引用服务】
保留dubbo的xml配置文件的方式,只需要在启动类上使用@ImportResource
注解引入xml文件位置。其余代码跟正常的web项目一样就可以了
使用api的方式(知道就行)
demo的需求
这里假设一个需求,有两个模块,一个的订单order模块,一个是用户user模块,我们在操作order服务的时候需要调用user服务的接口,获得用户的地址。
一、创建一个父项目,用来管理依赖,然后在父项目下面创建三个子项目
三个子项目分别是:api模块,order-service-consumer模块,user-service-provider模块
二、引入相应的依赖项
springboot整合dubbo的启动项
org.apache.dubbo
dubbo-spring-boot-starter
3.1.0
操作zookeeper的客户端
org.apache.curator
curator-framework
5.2.1
org.apache.curator
curator-recipes
5.2.1
org.apache.curator
curator-x-discovery
5.2.1
其余依赖:web依赖项,springboot依赖等等
三、在api模块中定义接口和实体类
UserService接口:根据用户id返回用户信息
OrderService接口:根据用户id从UserService中拿到用户地址,初始化订单
实体类UserAddress:用户地址信息
四、在user模块和order模块中分别实现相应的接口
user模块:
public class UserServiceImpl implements UserService {
@Override
public List getUserAddressList(String userId) {
System.out.println("调用了UserService");
List list = new ArrayList<>();
UserAddress address1 = new UserAddress(1, "重庆市万州区", "1", "wp", "173", "false");
UserAddress address2 = new UserAddress(1, "浙江省杭州市", "1", "wp1", "173", "false");
list.add(address1);
list.add(address2);
return list;
}
}
order模块:
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference
UserService service;
@Override
public List initOrder(String userId) {
List addressList = service.getUserAddressList("1");
for (UserAddress address : addressList) {
System.out.println(address.getUserAddress());
}
return addressList;
}
}
五、进行配置,暴露服务
首先在user和order的模块的启动项上面添加注解@EnableDubbo
表示支持dubbo
在user(服务提供方)的实现类上,添加注解@DubboService
,表示暴露该接口,向外提供服务
在order(服务调用方)的实现类中,给UserService添加注解@DubboReference
,表示给这个接口会自动从配置中心调用相应的服务
六、配置文件(application.properties)的编写
user模块的:
#应用的名称,就是注册到zookeeper的名字
dubbo.application.name=user-service-provider
#注册中心的地址:zookeeper可以改为其他的
dubbo.registry.address=zookeeper://localhost:2181
#user模块启动的服务端口号
server.port=8082
#协议名和协议的地址
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
order的:
#应用的名称
dubbo.application.name=order-service-consumer
#注册中心的地址
dubbo.registry.address=zookeeper://localhost:2181
#order模块启动的服务端口号
server.port=8081
七、然后补全模块的controller等,就可以正常运行了
使用接口测试工具,给order发送请求:localhost:8081/order/address
测试结果:order模块调用user模块成功,并且正常返回
跟第一种方式的区别仅仅是从第五步-六步(暴露服务)开始不一样。xml的方式不需要任何dubbo的注解,只需要正常的进行代码编写就好了,只需要在启动类加上@ImportResource就行;
引入了dubbo的starter或者dubbo的依赖都可以,还需要引入curator的依赖,跟上面一样
org.apache.dubbo
dubbo-spring-boot-starter
3.1.0
org.apache.dubbo
dubbo
2.7.0
操作zookeeper的客户端
org.apache.curator
curator-recipes
2.13.0
org.apache.curator
curator-framework
2.13.0
一、建立xml文件、添加@ImportResource
注解
二、编写消费者和服务者的接口实现
提供者
@Service//这个Service是为了让提供者自己调用该接口的注解,跟@Component一样
public class UserServiceImpl implements UserService {
@Override
public List getUserAddressList(String userId) {
System.out.println("调用了xml-UserService");
List list = new ArrayList<>();
UserAddress address1 = new UserAddress(1, "重庆交通大学", "1", "wp", "173", "false");
UserAddress address2 = new UserAddress(1, "浙江省杭州市", "1", "wp1", "173", "false");
list.add(address1);
list.add(address2);
return list;
}
}
消费者
@Service
public class OrderServiceImpl implements OrderService {
@Autowired//会调用远程的接口,自动注入
UserService service;
@Override
public List initOrder(String userId) {
List addressList = service.getUserAddressList("1");
for (UserAddress address : addressList) {
System.out.println(address.getUserAddress());
}
return addressList;
}
}
三、编写提供者的xml文件
四、编写消费者的xml
五、启动两个服务
访问消费者的controller,然后调用提供者的service,结果是:
在dubbo的官方文档中关于dubbo有很多的配置,这些配置有些是provider的有些是consumer的,在我们使用的时候常常需要这些配置。
在Spring中使用dubbo的时候,在xml文件中会有很多的配置,这里使用Springboot如果也需要配置该如何做呢?
我们按住Ctrl点击注解@DubboReference
和@DubboService
两个注解可以看到,这两个注解里面配有了很多的值可以供我们传递。所以直接在注解上面进行配置就可以完成相应的功能。
consumer启动的时候,在注册中心检查是否有对应的服务提供者,如果没有则抛出异常;@DubboReference(check = true)
默认:false
超时:timeout
@DubboReference(timeout = 3000)//设置超时为3秒
默认:1000(一秒)
consumer调用服务的时候,如果超过给定的毫秒数没有返回,则抛出异常
注意:timeout可以在提供方和调用方都设置,生效的规则是:1)精确的优先2)消费方优先
重试次数:retries
@DubboReference(retries = 3)
默认:-1
消费方第一次调用失败时,还会继续调用多少次,这里设置的是3,总共调用4次
多版本:version,灰度发布
@DubboService(version = "1.0.0")
默认:无
提供方指定版本,然后消费方也指定版本,就能进行指定版本的调用,实现灰度发布,同一个接口调用不同的实现
负载均衡策略
random 根据权重的随机调用方式
roundrobin 根据权重的轮询
leastActive 最少活跃树,上一次最快的服务器
consistentHash 一致性哈希
就是当前项目的应用的一些信息
name【必填】:当前应用名称,用于注册中心计算应用间依赖关系。
注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关。
logger:日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk。
同时如果有多个不同的注册中心,可以声明多个
标签,并在
或
的 registry
属性指定使用的注册中心。
address【必填】:注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔。
例如:ip:port,ip:port,不同集群的注册中心,请配置多个dubbo:registry标签
client:注册中心的客户端程序,我们公司的是:curator
timeout:注册中心请求超时时间(毫秒)
use-as-metadata-center:
同时,如果需要支持多协议,可以声明多个
标签,并在
中通过 protocol
属性指定使用的协议。
id:协议id,可以在
中引用此ID
name【必填】:协议名称
port:服务端口
dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果没有配置port,则自动采用默认端口,如果配置为-1,则会分配一个没有被占用的端口。Dubbo 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。
threads:服务线程池大小(固定大小),默认200
default:是否为缺省协议,用于多协议,默认false。
interface【必填】:服务接口名,就是实际项目中api中的那个接口的全类名
ref【必填】:服务接口的实现的bean的id
如果是springboot项目,就写注解@Component里面的id值;spring项目则需要再写一个
标签并指定id值
validation:是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验
serialization:协议序列化方式,当协议支持多种序列化方式时使用。
dubbo协议缺省为hessian2;rmi协议缺省为java;http协议缺省为json。比如:dubbo协议的dubbo,hessian2,java,compactedjava;以及http协议的json,xml等
connection:对每个提供者的最大连接数。默认100
rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数
id【必填】:服务引用的BeanId
interface【必填】:服务接口名,就是实际项目中api中的那个接口的全类名
protocol:只调用指定协议的服务提供方,其它协议忽略。
例如:dubbo协议
check:启动时检查提供者是否存在,true报错,false忽略。
group:服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致。
同时该标签为
和
标签的缺省值设置。如果
没有指定负载均衡策略,则会使用此标签的属性配置。
loadbalance:负载均衡策略。
可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用
filter:服务提供方远程调用过程拦截器名称,多个名称用逗号分隔
default:是否为缺省协议,用于多协议,默认false。
serialization:协议序列化方式,当协议支持多种序列化方式时使用。
dubbo协议缺省为hessian2;rmi协议缺省为java;http协议缺省为json。比如:dubbo协议的dubbo,hessian2,java,compactedjava;以及http协议的json,xml等
同时该标签为
标签的缺省值设置。
check:启动时检查提供者是否存在,true报错,false忽略。
validation:是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验
timeout:远程服务调用超时时间(毫秒)
loadbalance:负载均衡策略。
可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用
dubbo提供了四种负载均衡策略:random(随机), roundRobin(轮询), leastActive(最少活跃调用数), consistentHash (一致性 Hash)。默认为随机模式。
Random
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin
轮询,按公约后的权重设置轮询比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
缺省只对第一个参数 Hash,如果要修改,请配置
缺省用 160 份虚拟节点,如果要修改,请配置
在标签中使用loadbalance标签,指定对应的负载均衡方式
在消费端
提供者端
提供者方法的位置
客户端方法的位置
这里我们测试一下轮询的负载均衡方式:
首先启动两个个提供者和一个消费者(消费者配置轮询的负载均衡):
然后他们权重一样多,发送8次请求,所以一遍获得4次
然后将其中一个的权重加成200(加倍),发送12次请求,一个4次,一个8次
限流主要是为了性能调优,还可以结合重连次数,等方式来进行调优
通过设置服务提供者provider参数配置
iothreads: io线程池大小(固定大小)。限制的是io线程池大小,该线程池线程用于处理Dubbo框架自身业务逻辑,默认值是cpu个数+1,一般不会调整此参数。
threads:业务线程池大小(固定大小),就是Dubbo服务端处理请求的线程数,默认值200,默认使用固定大小线程池FixedThreadPool,队列为0。
executes:服务提供者每服务每方法最大可并行执行请求数,就是接口方法并发数量。默认值0表示不限制。
accepts:服务提供方最大可接受连接数,默认值0表示不限制。如果配置了数量则超过了会报错。
通过设置服务消费者consumer参数配置
服务消费者相关的配置都可以在@DubboReference注解中对属性直接配置。
connections:对每个提供者的最大连接数,对于dubbo等长连接的协议指的是可以建立的长连接个数。Dubbo协议默认值是1,即dubbo线程模型中的消费者单一长连接。
actives:每服务消费者每服务每方法最大并发调用数,默认值0表示无限制
当服务器压力剧增的情况下,对一些服务和页面有策略的不处理或用简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
mock=return {"code":1,"message":"熔断限流了"}
自定义一个返回的方式
可以使用指定 mock 属性值的方式来设置降级规则