重拾Dubbo

Posted 五小竹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重拾Dubbo相关的知识,希望对你有一定的参考价值。

楔子

第一次接触Dubbo, 是在毕业后第一家公司,那时候阿里停止了对Dubbo的维护,但是还是有相当多的公司在使用。当当在他的基础上开发Dubbox,而后Dubbo在沉寂几年之后,阿里又开始了对他的升级维护,2018 年 1 月 8 日发布 Dubbo 2.6.0 版本,与之前当当网开源的Dubbox 进行了合并,实现了 Dubbo 版本的统一整合。后来 阿里将Dubbo 开源贡献给 Apache,即 incubator-dubbo,如今已成为Apache的顶级项目。

重拾Dubbo
bq1.jpg

Dubbo入门

分布式系统

Web应用的演进

随着互联网的发展,Web应用的规模不断扩大,其发展经历了以下阶段

  • 单一应用架构 ,一个应用,将所有功能都部署在一起。
  • 垂直应用架构 ,将应用拆成互不相干的几个应用
  • 分布式服务架构 ,当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。 此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
  • 流动计算架构 ,当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。 此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

什么是分布式系统

分布式系统是若干个独立运行的系统的集合,使得用户使用起来像是在用一个系统。

Dubbo是什么

一款高性能、轻量级的开源 Java RPC 分布式服务框架。它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。RPC一般指远程过程调用。RPC是远程过程调用(Remote Procedure Call)的缩写形式

Dubbo能做什么

  • 当服务越来越多时,服务 URL 配置管理变得非常困难,F5 硬件负载均衡器的单点压力也越来越大。
  • 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
  • 服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?

Dubbo架构

模块 描述
Provider              服务提供者
Consumer 服务消费者
Registry 服务注册中心
Monitor 服务的调用次数和调用时间的监控中心
Container 服务运行容器
重拾Dubbo

调度过程

  • 0.start : 服务容器负责启动,加载,运行服务提供者。

  • 1.register : 服务提供者在启动时,向注册中心注册自己提供的服务。

  • 2.subscribe:服务消费者在启动时,向注册中心订阅自己所需的服务。

  • 5.count: 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo 整合SpringBoot 入门

以Zookeeper为注册中心,首先要安装并启动Zookeeper

项目结构

重拾Dubbo

服务者和消费者共同依赖的接口(dubbo-interface)

package com.wuzhu.service;

public interface HelloService {
   public String sayHello(String name);
}

服务提供者 (dubbo-provider)

dubbo-provider的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">

       <artifactId>dubbo-provider</artifactId>
       <groupId>com.wuzhu</groupId>
       <version>1.0-SNAPSHOT</version>
       <modelVersion>4.0.0</modelVersion>

       <parent>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-parent</artifactId>
           <version>2.1.0.RELEASE</version>
           <relativePath/> <!-- lookup parent from repository -->
       </parent>

       <properties>
           <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
           <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
           <java.version>1.8</java.version>
       </properties>

       <dependencies>
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-web</artifactId>
           </dependency>
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-test</artifactId>
               <scope>test</scope>
           </dependency>
           <!--服务提供接口-->
           <dependency>
               <groupId>com.wuzhu</groupId>
               <artifactId>dubbo-interface</artifactId>
               <version>1.0-SNAPSHOT</version>
           </dependency>
           <!--引入dubbo的依赖-->
           <dependency>
               <groupId>com.alibaba.spring.boot</groupId>
               <artifactId>dubbo-spring-boot-starter</artifactId>
               <version>2.0.0</version>
           </dependency>
           <!-- 引入zookeeper的依赖 -->
           <dependency>
               <groupId>com.101tec</groupId>
               <artifactId>zkclient</artifactId>
               <version>0.10</version>
           </dependency>
       </dependencies>

       <build>
           <plugins>
               <plugin>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-maven-plugin</artifactId>
               </plugin>
           </plugins>
       </build>

   </project>
dubbo-provider的配置文件application.yml

配置服务提供者的端口,和相应的dubbo配置,注册中心配置

server:
 port: 8847
spring:
 dubbo:
   application:
     name: dubbo-provider
   registry: zookeeper://localhost:2181

如果不使用Zookeeper注册中心,采用点对点直连,设置registry如下

server:
 port: 8847
spring:
 dubbo:
   application:
     name: dubbo-provider
   registry:
     register: false
实现接口

在服务提供方实现接口,注意这里使用的@Service注解是Dubbo的,不是Spring的。

package com.wuzhu.service;

import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

@Service
@Component
public class HelloServiceImpl implements HelloService {

   @Override
   public String sayHello(String name) {
       return "Hello, " + name;
   }
}

启动类
package com.wuzhu;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class DubboProviderApplication {
   public static void main(String[] args) {
       SpringApplication.run(DubboProviderApplication.class, args);
   }
}

服务消费者

dubbo-consumer的pom.xml

消费方和提供方都共同依赖dubbo-interface,dubbo-provider负责实现,dubbo-consumer端用来远程调用。

<?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>com.wuzhu</groupId>
    <artifactId>dubbo-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--引入接口项目jar包-->
        <dependency>
            <groupId>com.wuzhu</groupId>
            <artifactId>dubbo-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--引入dubbo的依赖-->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!-- 引入zookeeper的依赖 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
启动类
package com.wuzhu;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class DubboConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboConsumerApplication.class, args);
    }
}
消费者的配置application.yml

消费者同样也要注册到注册中心

server:
  port: 8848
spring:
  dubbo:
    application:
      name: dubbo-consumer
    registry: zookeeper://localhost:2181
服务调用

使用@Reference注解

package com.wuzhu.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.wuzhu.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Reference
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.sayHello("Dubbo");
    }

}

如果不使用注册中心,采用点对点直连的方式,可以添加参数url,指定服务提供方的url。去掉yml文件中的registry配置

    @Reference(url = "dubbo://localhost:8847")
    private HelloService helloService;

配置篇

启动时检测

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。可以通过 check="false" 关闭检查。关闭所有服务的启动时检查 (没有提供者时报错):

## 关闭某个服务的启动时检查
dubbo.reference.com.wuzhu.HelloService.check=false
## 关闭所有服务的启动时检查,关闭没有服务提供者时报错
dubbo.reference.check=false
dubbo.consumer.check=false
关闭注册中心启动时检查
dubbo.registry.check=false

配置覆盖关系

方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。官方文档给出了以设置timeout为例,配置覆盖的从高到低的关系如下图

重拾Dubbo

负载均衡

Dubbo 提供了多种均衡策略:

  • Random 随机,
  • RoundRobin 轮询,
  • LeastActive 最少活跃调用数,相同活跃数的随机
  • ConsistentHash,一致性 Hash,相同参数的请求总是发到同一提供者。

缺省为 random 随机调用

服务降级

什么是服务降级

服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。比如服务器上运行着三个服务,其中一个服务运行异常,造成大量线程堆积,占用服务器资源,导致其他两个服务无法正常运行,可以采取降级的措施,将异常的服务降级,保证了其他服务的正常运行。

Dubbo提供的两种服务降级
  • 强制返回为null,(mock=force:return+null)消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
  • 当调用失败时返回为null,( mock=fail:return+null) 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

向注册中心写入动态配置覆盖规则:

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));

集群容错

在集群调用失败时,Dubbo 提供了多种容错方案:

  • Failover Cluster(缺省为该模式) 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
<dubbo:service retries="2" />

<dubbo:reference retries="2" />

//设置方法的重试次数
<dubbo:reference>
    <dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
  • Failfast Cluster 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

  • Failsafe Cluster 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

  • Failback Cluster 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

  • Forking Cluster 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

  • Broadcast Cluster 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

配置集群容错

<dubbo:service cluster="failsafe" />

<dubbo:reference cluster="failsafe" />

支持多协议

Dubbo 支持dubbo、rmi、thrift、hessain、http,redis等多种协议,允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。

 <!-- 多协议配置 (不同服务不同协议) -->
    <dubbo:protocol name="dubbo" port="20880" />
    <dubbo:protocol name="rmi" port="1099" />
    <!-- 使用dubbo协议暴露服务 -->
    <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
    <!-- 使用rmi协议暴露服务 -->
    <dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
    
    <!-- 多协议配置 多协议暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <dubbo:protocol name="hessian" port="8080" />
    <!-- 使用多个协议暴露服务 -->
    <dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />

序列化

dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。默认hessian序列化协议。

并发控制

Dubbo 中的并发控制,可以通过配置service的executes参数和actives来实现控制 比如:限制 HelloService 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个:

<dubbo:service interface="com.wuzhu.HelloService" executes="10" />

限制 HelloService 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个:

<dubbo:service interface="com.wuzhu.HelloService" actives="10" />

或Dubbo的@Service注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface Service {
    Class<?> interfaceClass() default void.class;
    ......
    int executes() default 0;
    ......
    int actives() default 0;
    }

尾声

相比与近些年流行的SpringCloud,Dubbo更关注于服务治理RPC框架,使用 Netty 这个的NIO框架, 底层 TCP 协议传输, Hession 序列化实现 RPC。而SpringCloud则是一套微服务系统解决方案,由多种组件构成。Dubbo官方文档从入门到配置,再到原理介绍的比较全面。这里建议大家在学习的时候多查阅。

参考资料:官方文档





点个在看你最好看


以上是关于重拾Dubbo的主要内容,如果未能解决你的问题,请参考以下文章

重拾博客-《立下目标》

重拾博客-《立下目标》

稳定性 耗时 监控原因分析-- dubbo rpc 框架 的线程池,io 连接模型. 客户端,服务端

重拾面向对象软件设计

带你重拾JavaScript之console的你所不知道的功能

造车新势力回港:蔚来能重拾“老大哥”头衔吗?