dubbo
Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护,Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
1. 基本概念
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式
- 在客户端将对象进行序列化
- 底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
- 服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
- 对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
- 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次请求

节点角色
节点 | 角色说明 |
Provider | 服务的提供方 |
Consumer | 服务的消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 监控服务的统计中心 |
Container | 服务运行容器 |
调用关系
- 服务容器负责启动,加载,运行服务提供者
- 服务提供者在启动时,向注册中心注册自己提供的服务
- 服务消费者在启动时,向注册中心订阅自己所需的服务
- 在注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
2. dubbo入门
- 注册中心:zookeeper作为注册中心
- 注册中心负责服务地址的注册与查找,相当于目录服务
- 服务提供者和消费者只在启动时与注册中心交互,注册中不转发请求,压力较小
- Zookeeper是apache hadoop的子项目,是一个树形的目录服务,支持变更推送,适合作为dubbo的服务注册中心,工业强度较高,可用于生产环境
- 服务提供方
- pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>dubbo-quick</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <spring.version>5.0.6.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!--dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.5.7</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.11.0.GA</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven </groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8001</port> <path>/</path> </configuration> <executions> <execution> <!-- 打包完成后,运行服务 --> <phase>package</phase> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
- 服务方服务
package com.service.impl; import com.alibaba.dubbo.config.annotation.Service; import com.service.HelloService; @Service public class HelloServiceImp implements HelloService { public String sayHello(String name) { return "hello "+name; } }
-
- 服务方spring配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--1.服务提供方在zookeeper中的“别名”--> <dubbo:application name="dubbo-server"/> <!--2.注册中心的地址--> <dubbo:registry address="zookeeper://192.168.80.128:2181"/> <!--3.扫描类(将什么包下的类作为服务提供类)--> <dubbo:annotation package="com.service.impl"/> </beans>
-
- 服务方web配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <listener> <!--启动上下文监听--> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-server.xml</param-value> </context-param> </web-app>
- 消费方
- pom.xml,与服务方一致,修改Tomcat端口8002
- 消费方controller
package com.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.com.service.HelloService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { @Reference private HelloService helloService; @RequestMapping("/hello") @ResponseBody public String hello(String name){ helloService.sayHello(name); return "ok"; } }
- 消费方接口
package com.com.service; public interface HelloService { public String sayHello(String name); }
- 消费方springMVC
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--Dubbo的应用名称,通常使用项目名 --> <dubbo:application name="dubbo-consumer" /> <!--配置Dubbo的注册中心地址 --> <dubbo:registry address="zookeeper://192.168.80.128:2181" /> <!--配置Dubbo扫描类,将这个类作为服务进行发布 --> <dubbo:annotation package="com.controller" /> </beans>
- 消费方web
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springMVC.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- 监控中心
- 服务管理端解压 dubbo-admin-master.zip ,修改配置文件 ,
- 返回到项目根目录,使用maven打包:mvn clean package
- 在dos下运行target目录中的jar文件:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
- 浏览器输入:http://localhost:7001/ ,第一次访问时,需要登录,帐号密码都是root
- 监控统计中心,记录服务被调用多少次
- 解压dubbo-monitor-simple-2.5.3.zip, 修改dubbo-monitor-simple-2.5.3\conf\dubbo.properties
-
双击运行dubbo-monitor-simple-2.5.3\bin\start.bat
-
分别修改dubbo-server和dubbo-consumer的spring.xml,加入下面标签
- 解压dubbo-monitor-simple-2.5.3.zip, 修改dubbo-monitor-simple-2.5.3\conf\dubbo.properties
<!-- 让监控 去注册中心 自动找服务 --> <dubbo:monitor protocol="registry"/>
3. 配置说明
- 启动时检查,默认为true,启动时会在注册中心检查依赖的服务是否可用,不可用时会抛出异常,在消费方编写初始化容器的main方法启动
<!--默认是true:抛异常;false:不抛异常--> <dubbo:consumer check="false" />
- 超时时间:由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时),为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
<!--设置超时时间为2秒,默认为1秒--> <dubbo:provider timeout="2000"/
- 重试次数:当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值是2次,我们可以自行设置,总共尝试n+1次
<!-- 消费方连接第1次不算,再来重试3次,总共重试4次 --> <dubbo:provider timeout="2000" retries="3"/>
-
- 并不是所有的方法都适合设置重试次数
- 幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
- 非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
- 并不是所有的方法都适合设置重试次数
<!--单独设置某个方法--> <dubbo:reference interface="service.HelloService" id="helloService"> <dubbo:method name="sayHello" retries="3"/> <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 --> </dubbo:reference>
- 多版本:一个接口多个版本的实现类可以用版本号规定使用哪个版本,控制层要改为自动注入,因为@Reference注解和 <dubbo:reference>在这里冲突,当消费者的版本修改为 version="*",那么就会随机调用服务提供者的版本
<dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl01" version="1.0.0"/> <dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl02" version="2.0.0"/>
<dubbo:reference interface="service.HelloService" id="helloService" version="2.0.0"> <dubbo:method name="sayHello" retries="3"/> <dubbo:method name="sayNo" retries="0"/> </dubbo:reference>
- 本地存根:先在消费者处理一些业务逻辑,再调用提供者的过程,就是“本地存根” ,必须使用构造方法的方式注入
public class HelloServiceStub implements HelloService { private HelloService helloService; // 注入HelloService public HelloServiceStub(HelloService helloService) { this.helloService = helloService; } p ublic String sayHello(String name) { System.out.println("本地存根数据验证。。。"); if(!StringUtils.isEmpty(name)){ return helloService.sayHello(name); } r eturn "i am sorry!"; } p ublic String sayNo() { return helloService.sayNo(); } }
<dubbo:reference interface="service.HelloService" id="helloService" version="1.0.0" stub="service.impl.HelloServiceStub"> <dubbo:method name="sayHello" retries="3"/> <dubbo:method name="sayNo" retries="0"/> </dubbo:reference>
4.负载均衡策略
dubbo一共提供4种策略,缺省为 random 随机分配调用,可以通过服务管理端进行权重分配
5.高可用
- zookeeper注册中心宕机,可以消费dubbo暴露的服务 ,监控中心宕掉不影响使用,只是丢失部分采样数据数据库宕掉后
- 注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复\
- 关闭zookeeper消费者仍然可以正常消费
6. 服务降级
根据实际的情况和流量,对一些服务有策略的停止或换种简单的方式处理,从而释放服务器的资源来保证核心业务的正常运行
- 实现方式:在 管理控制台配置服务降级
- 屏蔽:mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响
- 容错:mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响