Dubbo简介
源自于阿里巴巴的开源的轻量级的java RPC 框架,可以与spring框架无缝集成
RPC 全称为remote procedure call 及 远程调用
Dubbo架构
dubbo架构里有五个重要的节点,(Registry,provider,consumer,monitor,container)。
Registry | 注册服务提供者和服务消费者的服务注册和发现中心,是注册中心 |
provider | 提供服务的服务提供者 |
consumer | 进行远程调用服务的服务消费者 |
container | 启动服务的容器 |
monitor | 统计远程服务调用的次数和调用时间的监控中心 |
虚线都是异步访问,实线是同步访问,蓝色虚线是在服务启动时完成,红色虚线是在服务运行中执行的
0:服务容器负责启动服务,启动服务提供者。
1:服务提供者启动时,把提供的服务注册到注册中心。
2:服务消费者启动时,从注册中心订阅自己所需要的服务。
3:注册中心返回提供者的服务地址列表给消费者,如果服务有所变更,注册中心将基于长连接推送更新数据给消费者。
4:消费者从提供者的服务地址列表,基于随机调用负载均衡算法,选择需要的服务进行调用,如果失败,则选择另一台服务调用。
5:服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
注册中心zookeeper的使用
zookeeper是hadoop的一个子项目,十大数据技术产品中的一个产品是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
服务提供者(Provider)启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
服务消费者(Consumer)启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
监控中心(Monitor)启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址
zookeeper使用命令
进入zookeeper的bin目录
开启zookeeper:./zkServer.sh start
停止zookeeper:./zkServer.sh stop
查看服务状态:./zkServer.sh status
连接客户端:./zkCli.sh (加端口号,不加则连接本地)
Dubbo案例的重要知识点
dubbo的xml配置
在web.xml中加载spring的xml文件
<!--加载spring配置文件,该文件用来向注册中心注册应用服务和确定协议等-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</context-param>
<!--设置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--服务消费者的web.xml中多一个配置servlet的步骤,用来处理调用远程服务的请求-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-web.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
注册服务提供者
<!--注册服务提供者-->
<dubbo:application name="dubbo-provider" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://192.168.10.130:2181"/>
<!-- 注册 协议和port -->
<dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>
<!-- 扫描指定包,加入@Service注解的类会被发布为服务 -->
<dubbo:annotation package="com.yl.service.impl" />
注册服务消费者
<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
<dubbo:application name="dubbo-consumer" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://192.168.10.130:2181"/>
<!-- 扫描指定包,加入@Controller注解的类会被发布为服务 -->
<dubbo:annotation package="com.yl.controller" />
在消费者工程的开发中
controller中要定义一个远程调用的接口,添加@Reference注解
@RestController
@RequestMapping("demo")
public class HelloController {
@Reference
private HelloSerivce serivce;
@RequestMapping("/hello")
public String sayHello(String name){
String res = serivce.sayHello(name);
System.out.println(res);
return res;
}
}
我们可以提高代码的高可复用性,为了避免代码的耦合度高,可以将接口单独放在一个maven工程中,这样,就不需要在消费者工程中再复写接口类,直接添加对应的maven坐标即可。
包的扫描
服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类,来发布服务和引用服务
协议
<dubbo:protocol name="dubbo" port="20880"/>
一般情况下协议选用”dubbo“,还有rmi、hessian、http、webservice、rest、redis等
dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机 器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求 量很低。
也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,比如:
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.api.HelloService" ref="helloService"
protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.api.DemoService" ref="demoService"
protocol="rmi" />
负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询、最少活跃调用数、一致性 Hash),缺省为random随机调用。
配置负载均衡属性:
提供方配置:在@Service注解中用loadbalance属性
消费方配置:在@Reference注解中check属性设置false,loadbalance属性
解决dubbo无法发布被事务代理的service类的问题
问题原因:在事务代理时,spring默认采用基于jdk的动态代理方式来创建代理对象,代理对象的完整类名是com.sun.proxy$42,导致跟dubbo配置文件中标签扫描的路径下的类名不匹配,则无法发布服务
问题解决:解决方式操作步骤:
(1)修改applicationContext-service.xml配置文件,开启事务控制注解支持时指定proxy-target-class 属性,值为true。其作用是使用cglib代理方式为Service类创建代理对象
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
(2)修改HelloServiceImpl类,在Service注解中加入interfaceClass属性,值为HelloService.class, 作用是指定服务的接口类型
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}