Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护
Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
简单来说,dubbo是个服务框架,如果没有分布式的需求,是不需要用的

RPC基本的通信原理

  1. 在客户端将对象进行序列化
  2. 底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
  3. 服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
  4. 对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
  5. 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次请求

架构图

image.png

依赖

不同版本之间可能出现协调性问题,这套依赖比较不错

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>${spring.version}</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-core</artifactId>
  10. <version>${spring.version}</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-beans</artifactId>
  15. <version>${spring.version}</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework</groupId>
  19. <artifactId>spring-context</artifactId>
  20. <version>${spring.version}</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework</groupId>
  24. <artifactId>spring-context-support</artifactId>
  25. <version>${spring.version}</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework</groupId>
  29. <artifactId>spring-tx</artifactId>
  30. <version>${spring.version}</version>
  31. </dependency>
  32. <!--dubbo -->
  33. <dependency>
  34. <groupId>com.alibaba</groupId>
  35. <artifactId>dubbo</artifactId>
  36. <version>2.5.7</version>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.apache.zookeeper</groupId>
  40. <artifactId>zookeeper</artifactId>
  41. <version>3.4.6</version>
  42. </dependency>
  43. <dependency>
  44. <groupId>com.github.sgroschupf</groupId>
  45. <artifactId>zkclient</artifactId>
  46. <version>0.1</version>
  47. </dependency>
  48. <dependency>
  49. <groupId>javassist</groupId>
  50. <artifactId>javassist</artifactId>
  51. <version>3.11.0.GA</version>
  52. </dependency>
  53. </dependencies>

常用配置

记录一些很常用的配置

启动时检查

启动时会在注册中心检查依赖的服务是否可用,不可用时会抛出异常
在消费方编写初始化容器的main方法启动(tomcat启动方式,必须访问一次action才能初始化 spring)

  1. public class Test {
  2. public static void main(String[] args) {
  3. final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  4. try {
  5. System.in.read();
  6. } catch (IOException e) {
  7. e.printStackTrace();
  8. }
  9. }
  10. }

还需要log4j 放在消费者的resource文件夹下

  1. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  2. log4j.appender.stdout.Target=System.out
  3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  4. log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
  5. log4j.appender.file=org.apache.log4j.FileAppender
  6. log4j.appender.file.File=dubbo.log
  7. log4j.appender.file.layout=org.apache.log4j.PatternLayout
  8. log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
  9. log4j.rootLogger=error,stdout,file

之后在不启动服务方的前提下启动消费者,会抛异常。
如果不想要异常,可以在spring文件里加

  1. <dubbo:consumer check="false" />

超时机制

由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
在服务提供者添加如下配置:

  1. <!--设置超时时间为2秒,默认为1秒-->
  2. <dubbo:provider timeout="2000"/>

配置原则

dubbo推荐在Provider上尽量多配置Consumer端属性:

  1. 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试
    次数,等等
  2. 在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可
    以作消费者的缺省值。

重试次数

当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值是2次,我们可以自行设置
在provider提供方配置:

  1. <!-- 消费方连接第1次不算,再来重试3次,总共重试4 -->
  2. <dubbo:provider timeout="2000" retries="3"/>

并不是所有的方法都适合设置重试次数
幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)

定制只在某些方法上重试

  1. <dubbo:reference interface="service.HelloService" id="helloService">
  2. <dubbo:method name="sayHello" retries="3"/>
  3. <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
  4. </dubbo:reference>

多版本

一个接口,多个(版本的)实现类,可以使用定义版本的方式引入
为HelloService接口定义两个实现类,提供者修改配置:

  1. <dubbo:service interface="service.HelloService"
  2. class="service.impl.HelloServiceImpl01" version="1.0.0"/>
  3. <dubbo:service interface="service.HelloService"
  4. class="service.impl.HelloServiceImpl02" version="2.0.0"/>
  1. 消费者可以自己选择版本
  1. <dubbo:reference interface="service.HelloService" id="helloService" version="2.0.0">
  2. <dubbo:method name="sayHello" retries="3"/>
  3. <dubbo:method name="sayNo" retries="0"/>
  4. </dubbo:reference>
  1. 注意:消费者的控制层要改为自动注入,因为@Reference注解和 在这里冲突<br />当消费者的版本修改为 version="*",那么就会随机调用服务提供者的版本

本地存根

一般来说客户端可能只有接口,实现全在服务端。但是这样不是永远方便。有时我们也需要在客户端做些简单的逻辑。这就叫本地存根。
我们可以自己写个类实现接口,然后把service注入进来。注意这里的注入必须以构造方法的形式注入!

  1. public class HelloServiceStub implements HelloService {
  2. private HelloService helloService;
  3. // 注入HelloService
  4. public HelloServiceStub(HelloService helloService) {
  5. this.helloService = helloService;
  6. }
  7. public String sayHello(String name) {
  8. System.out.println("本地存根数据验证。。。");
  9. if(!StringUtils.isEmpty(name)){
  10. return helloService.sayHello(name);
  11. }
  12. return "i am sorry!";
  13. }
  14. public String sayNo() {
  15. return helloService.sayNo();
  16. }
  17. }
  1. 还要修改配置
  1. <dubbo:reference interface="service.HelloService" id="helloService"
  2. version="1.0.0" stub="service.impl.HelloServiceStub">
  3. <dubbo:method name="sayHello" retries="3"/>
  4. <dubbo:method name="sayNo" retries="0"/>
  5. </dubbo:reference>

负载均衡

image.png
消费方修改权重

  1. <dubbo:reference loadbalance="roundrobin" interface="service.HelloService"
  2. id="helloService" version="2.0.0" stub="stub.HelloServiceStub">
  3. <dubbo:method name="sayHello" retries="3"/>
  4. <dubbo:method name="sayNo" retries="0"/>
  5. </dubbo:reference>

高可用

zookeeper挂了还能使用已经注册的服务,就是不能再用新的了。

服务降级

为什么要使用服务降级,这是防止分布式服务发生雪崩效应
什么是雪崩?就是蝴蝶效应,当一个请求发生超时,一直等待着服务响应,那么在高并发情况下,很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,而宕机之后会导致分布式其他 服务调用该宕机的服务也会出现资源耗尽宕机,这样下去将导致整个分布式服务都瘫痪,这就是雪崩。

在管理控制台配置服务降级:
屏蔽和容错
屏蔽:mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
容错:mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。