Dubbo 版本 :

  Dubbo 社区目前主力维护的有 2.6.x 和 2.7.x 两大版本,其中,

  • 2.6.x 主要以 bugfix 和少量 enhancements 为主,因此能完全保证稳定性
  • 2.7.x 作为社区的主要开发版本,得到持续更新并增加了大量新 feature 和优化,同时也带来了一些稳定性挑战

  Apache dubbo 官网已经出现3.0版本。目前官网展示版本为 2.7 与3.0.如若下列官网地址打不开,可能是项目更新了,进入 IT虾米网 自行查阅

  版本更多信息请参考官网。相信小伙伴们对于Dubbo 都有一定的了解。相关基础知识点可以参考 IT虾米网 .

Dubbo 再聚首之自动化配置:

dubbo-spring-boot-starter(org.apache.dubbo:2.7.7):

  基于目前的 Spring Boot 自动化配置的盛行,我们在使用 Dubbo的时候不再像以前集成 spring 的时候那样的繁琐,需要进行很多的配置。接下来来体验一下 dubbo-spring-boot-starter 带来的便捷。

  本文注册中心采用 Spring  Cloud  Alibaba  Nacos ,不熟悉的小伙伴可以参考 IT虾米网

项目目录:

  springboot-dubbo-api 模块:

1.构建服务接口,api模块,导入 Rest 协议支持依赖:

<dependencies> 
        <!--添加REST支持--> 
        <!--Rest协议--> 
        <dependency> 
            <groupId>org.jboss.resteasy</groupId> 
            <artifactId>resteasy-jaxrs</artifactId> 
            <version>3.8.1.Final</version> 
        </dependency> 
        <dependency> 
            <groupId>org.jboss.resteasy</groupId> 
            <artifactId>resteasy-client</artifactId> 
            <version>4.0.0.Final</version> 
        </dependency> 
        <dependency> 
            <groupId>org.eclipse.jetty</groupId> 
            <artifactId>jetty-server</artifactId> 
            <version>9.4.12.RC2</version> 
        </dependency> 
        <dependency> 
            <groupId>org.eclipse.jetty</groupId> 
            <artifactId>jetty-servlet</artifactId> 
            <version>9.4.12.RC2</version> 
        </dependency> 
    </dependencies>

2. 创建接口 :

@Path("/") 
public interface HelloService { 
     
    @GET 
    @Path("/sayRest") 
    String sayHello() throws Exception; 
}

  springboot-dubbo-provider 模块:

1.导入依赖:

<dependencies> 
        <!--基于spring-boot-dependencies 2.3.0RELEASE 版本 --> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter</artifactId> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-test</artifactId> 
            <scope>test</scope> 
        </dependency> 
        <!-- dubbo 依赖--> 
        <dependency> 
            <groupId>org.apache.dubbo</groupId> 
            <artifactId>dubbo-spring-boot-starter</artifactId> 
            <version>2.7.7</version> 
        </dependency> 
        <!--nacos注册中心依赖--> 
        <dependency> 
            <groupId>com.alibaba.nacos</groupId> 
            <artifactId>nacos-client</artifactId> 
            <version>1.2.1</version> 
        </dependency> 
        <!--zk注册中心依赖--> 
        <dependency> 
            <groupId>org.apache.curator</groupId> 
            <artifactId>curator-recipes</artifactId> 
            <version>4.0.1</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.curator</groupId> 
            <artifactId>curator-framework</artifactId> 
            <version>4.0.1</version> 
        </dependency> 
        <dependency> 
            <artifactId>springboot-dubbo-api</artifactId> 
            <groupId>com.wuzz.demo</groupId> 
            <version>1.0-SNAPSHOT</version> 
        </dependency> 
    </dependencies>

3. 创建服务实现类 :

@DubboService(loadbalance = "random", // 负载均衡 
        timeout = 50000, //超时 
        cluster = "failsafe", // 服务容错 
        protocol = {"dubbo", "rest"}, //多协议支持 
        registry = {"hangzhou", "wenzhou"} //多注册中心 
) 
public class HelloServiceImpl implements HelloService { 
    @Override 
    public String sayHello() throws Exception { 
        return "Hello Dubbo"; 
    } 
}

4. 配置文件配置

spring.application.name=springboot-dubbo 
 
# Netty -> 
dubbo.protocols.dubbo.name=dubbo 
dubbo.protocols.dubbo.port=-1 
 
# jetty (配置了rest协议) 
dubbo.protocols.rest.name=rest 
dubbo.protocols.rest.port=-1 
dubbo.protocols.rest.server=jetty 
 
# zk注册中心 
dubbo.registries.hangzhou.address=zookeeper://192.168.1.101:2181 
dubbo.registries.hangzhou.timeout=10000 
dubbo.registries.hangzhou.default=true 
## 服务启动的时候,如果注册中心有问题,那么服务就启动失败 
dubbo.registries.hangzhou.check=false 
# nacos 注册中心 
dubbo.registries.wenzhou.address=nacos://localhost:8848

5. 服务启动类,配置扫描路径

@DubboComponentScan(basePackages = "com.wuzz.demo") //dubbo服务扫描 
@SpringBootApplication 
public class SpringBootDubboProviderApp { 
 
    private final static Logger log = LoggerFactory.getLogger(SpringBootDubboProviderApp.class); 
 
    public static void main(String[] args) { 
        SpringApplication.run(SpringBootDubboProviderApp.class, args); 
        log.info("服务启动成功"); 
 
    } 
}

  springboot-dubbo-client 模块:

1.导入相关依赖:

<dependencies> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter</artifactId> 
        </dependency> 
        <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>org.apache.dubbo</groupId> 
            <artifactId>dubbo-spring-boot-starter</artifactId> 
            <version>2.7.7</version> 
        </dependency> 
        <dependency> 
            <groupId>com.alibaba.nacos</groupId> 
            <artifactId>nacos-client</artifactId> 
            <version>1.2.1</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.curator</groupId> 
            <artifactId>curator-recipes</artifactId> 
            <version>4.0.1</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.curator</groupId> 
            <artifactId>curator-framework</artifactId> 
            <version>4.0.1</version> 
        </dependency> 
        <dependency> 
            <artifactId>springboot-dubbo-api</artifactId> 
            <groupId>com.wuzz.demo</groupId> 
            <version>1.0-SNAPSHOT</version> 
        </dependency> 
    </dependencies>

2. 创建测试类 :

@RestController 
public class DubboController { 
    //Dubbo提供的注解 
    @DubboReference(loadbalance = "roundrobin", 
              timeout = 9000, cluster = "failfast", 
              mock = "com.wuzz.demo.mock.HelloServiceMock", check = false) 
    HelloService helloService; 
 
    @GetMapping("/sayhello") 
    public String sayHello() throws Exception { 
        return helloService.sayHello(); //我调用这个服务可能失败,如果失败了,我要怎么处理 
    } 
 
    // dubbo 泛化调用  
    @DubboReference(interfaceName = "com.wuzz.demo.api.HelloService",generic = true,check = false) 
    GenericService genericService; 
 
    @GetMapping("/demo") 
    public String demo(){ 
        return genericService.$invoke("sayHello",new String[0],null).toString(); 
    } 
}

  mock 实现类:

public class HelloServiceMock implements HelloService { 
    @Override 
    public String sayHello() { 
        return "服务端发生异常, 被降解了。返回兜底数据。。。"; 
    } 
}

3.配置文件,启动类无需任何配置

spring.application.name=springboot-dubbo-client 
dubbo.registry.address=nacos://localhost:8848 
server.port=8889

  然后先后启动 服务提供者、服务消费者模块。可以看到 注册中心应当有两个服务的相关注册信息:

  然后就可以访问对应的接口进行测试。

spring-cloud-starter-dubbo(org.apache.dubbo:2.7.6):

  与springboot 集成不同,spring-cloud-alibaba 自成生态,在多注册中心的用法上有兼容问题。

项目目录

  spring-cloud-alibaba-dubbo-api 模块:

1.添加相关接口

public interface HelloService { 
 
    String sayHello() throws Exception; 
}

  spring-cloud-alibaba-dubbo-provider 模块:

1.添加相关依赖:

<dependencies> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter</artifactId> 
        </dependency> 
        <!--dubbo 依赖--> 
        <dependency> 
            <groupId>com.alibaba.cloud</groupId> 
            <artifactId>spring-cloud-starter-dubbo</artifactId> 
            <version>2.2.1.RELEASE</version> 
        </dependency> 
        <!--nacos 注册中心依赖--> 
        <dependency> 
            <groupId>com.alibaba.cloud</groupId> 
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> 
            <version>2.2.1.RELEASE</version> 
        </dependency> 
        <dependency> 
            <groupId>com.wuzz.demo</groupId> 
            <artifactId>spring-cloud-alibaba-dubbo-api</artifactId> 
            <version>1.0-SNAPSHOT</version> 
        </dependency> 
    </dependencies>

2.实现类:

@Service(loadbalance = "random",timeout = 50000,cluster = "failsafe") 
public class HelloServiceImpl implements HelloService { 
    @Override 
    public String sayHello() throws Exception { 
        return "Hello Dubbo"; 
    } 
}

3. 配置文件配置:

spring.application.name=springboot-dubbo 
dubbo.scan.base-packages=com.wuzz.demo 
dubbo.protocol.port=20882 
dubbo.protocol.name=dubbo 
spring.cloud.nacos.discovery.server-addr=localhost:8848

4.启动类:

@EnableDiscoveryClient 
@SpringBootApplication 
public class SpringCloudAlibabaDubboProviderApp { 
 
    private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboProviderApp.class); 
 
    public static void main(String[] args) { 
        SpringApplication.run(SpringCloudAlibabaDubboProviderApp.class, args); 
        log.info("服务启动成功"); 
 
    } 
}

  spring-cloud-alibaba-dubbo-client 模块:

1.导入依赖:

<dependencies> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter</artifactId> 
        </dependency> 
        <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.alibaba.cloud</groupId> 
            <artifactId>spring-cloud-starter-dubbo</artifactId> 
            <version>2.2.1.RELEASE</version> 
        </dependency> 
        <dependency> 
            <groupId>com.alibaba.cloud</groupId> 
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> 
            <version>2.2.1.RELEASE</version> 
        </dependency> 
        <dependency> 
            <groupId>com.wuzz.demo</groupId> 
            <artifactId>spring-cloud-alibaba-dubbo-api</artifactId> 
            <version>1.0-SNAPSHOT</version> 
        </dependency> 
    </dependencies>

3.测试类编写:

@RestController 
public class DubboController { 
    //Dubbo提供的注解 
    @Reference(loadbalance = "roundrobin", 
            timeout = 1, cluster = "failfast", 
            mock = "com.wuzz.demo.mock.HelloServiceMock", check = false) 
    HelloService helloService; 
 
    @GetMapping("/sayhello") 
    public String sayHello() throws Exception { 
        return helloService.sayHello(); //我调用这个服务可能失败,如果失败了,我要怎么处理 
    } 
}

  mock 实现类:

public class HelloServiceMock implements HelloService { 
    @Override 
    public String sayHello() { 
        return "服务端发生异常, 被降解了。返回兜底数据。。。"; 
    } 
}

4.配置文件:

spring.application.name=springboot-dubbo-client 
server.port=8889 
spring.cloud.nacos.discovery.server-addr=localhost:8848

5.启动类:

@EnableDiscoveryClient 
@SpringBootApplication 
public class SpringCloudAlibabaDubboClientApp { 
 
    private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboClientApp.class); 
 
    public static void main(String[] args) { 
        SpringApplication.run(SpringCloudAlibabaDubboClientApp.class, args); 
        log.info("服务启动成功"); 
    } 
}

  先后启动服务提供者、服务消费者进行测试即可。

Dubbo 常用功能简介:

多协议支持:

  • dubbo://  :Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
  • rmi://      : RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。
  • hessian://  : Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
  • http://     :基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 
  • webservice://    :基于 WebService 的远程调用协议,基于 Apache CXF  的 frontend-simple 和 transports-http 实现 。
  • thrift://    :当前 dubbo 支持 的 thrift 协议是对 thrift 原生协议 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。
  • memcached://   :基于 memcached  实现的 RPC 协议 。
  • redis://   :基于 Redis  实现的 RPC 协议 。
  • rest://    :基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持
  • grpc://    :Dubbo 自 2.7.5 版本开始支持 gRPC 协议,对于计划使用 HTTP/2 通信,或者想利用 gRPC 带来的 Stream、反压、Reactive 编程等能力的开发者来说, 都可以考虑启用 gRPC 协议。

Dubbo 的负载均衡:

  在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。

  1.Random LoadBalance:

  • 随机,按权重设置随机概率。
  • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

  2.RoundRobin LoadBalance:

  • 轮询,按公约后的权重设置轮询比率。
  • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

  3.LeastActive LoadBalance:

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
  • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

  4.ConsistentHash LoadBalance:

  • 一致性 Hash,相同参数的请求总是发到同一提供者。
  • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
  • 算法参见:IT虾米网
  • 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
  • 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />

  5.ShortestResponse LoadBalance:

  最短响应时间负载均衡算法,筛选成功调用响应时间最短的调用程序的数量,并计算这些调用程序的权重和数量。然后根据响应时间的长短来分配目标服务的路由权重。

集群容错:

  在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。

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

  • Failfast Cluster  :快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

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

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

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

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

服务降级:

  dubbo的降级方式: Mock。上文代码中已给出示例实现步骤:

  1. 在client端创建一个 HelloServiceMock 类,实现对应的接口(需要对哪个接口进行mock,就实现哪个),名称必须以Mock结尾
  2. 在client端的服务调用的注解配置中,添加 mock 配置,增加一个mock属性指向创建的HelloServiceMock 
  3. 模拟错误(设置timeout),模拟超时异常,运行测试代码即可访问到HelloServiceMock 这个类。当服务端故障解除以后,调用过程将恢复正常,

Dubbo泛化:

  泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。 上文已给出示例。

  更多的泛化配置可以参考官网 :IT虾米网

主机绑定:

  关于主机绑定的源码实现位于  org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol

String host = findConfigedHosts(protocolConfig, registryURLs, map); 
Integer port = findConfigedPorts(protocolConfig, name, map);

  主机绑定的步骤主要有以下几个步骤:

  •  查找环境变量中是否存在启动参数 [DUBBO_IP_TO_BIND] =服务注册的ip
  • 读取配置文件, dubbo.protocols.dubbo.host= 服务注册的ip
  • InetAddress.getLocalHost().getHostAddress() 获得本机ip地址
  • 通过Socket去连接注册中心,从而获取本机IP
  • 会轮询本机的网卡,直到找到合适的IP地址
  • 上面获取到的ip地址是bindip,如果需要作为服务注册中心的ip, DUBBO_IP_TO_REGISTRY -dDUBBO_IP_TO_REGISTRY=ip

配置优先级:

  • 方法层面的配置要优先于接口层面的配置, 接口层面的配置要优先于全局配置.
  • 如果级别一样,以客户端的配置优先,服务端次之.

性能调优的参数:

  dubbo 提供了针对服务端/客户端的相关参数调优,以下列举了一些比较重要的参数。

@Configuration 
public class DubboConfig { 
 
    //服务端相关调优参数 
    @Bean 
    public ProviderConfig providerConfig() { 
        ProviderConfig config = new ProviderConfig(); 
        //默认200 服务线程池大小(固定大小) 
        config.setThreads(200); 
        //默认CPU + 1 
        //IO线程池,接收网络读写中断,以及序列化和反序列化, 
        // 不处理业务,业务线程池参见threads配置,此线程池和CPU相关,不建议配置。 
        config.setIothreads(Runtime.getRuntime().availableProcessors() + 1); 
        //线程池类型,可选:fixed/cached/limit(2.5.3以上)/eager(2.6.x以上) 
        config.setThreadpool("fixed"); 
        //对每个提供者的最大连接数,rmi、http、hessian 
        //等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数 
        config.setConnections(0); 
        //线程池队列大小,当线程池满时,排队等待执行的队列大小, 
        //建议不要设置,当线程池满时应立即失败,重试其它服务提供机器, 
        //而不是排队,除非有特殊需求。 
        config.setQueues(0); 
        //每服务消费者每服务每方法最大并发调用数 
        config.setAccepts(0); 
        //服务提供者每服务每方法最大可并行执行请求数 
        config.setExecutes(0); 
        return config; 
 
    } 
    //客户端相关调优参数 
    @Bean 
    public ConsumerConfig consumerConfig() { 
        ConsumerConfig config = new ConsumerConfig(); 
        //每个服务对每个提供者的最大连接数, 
        //rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置 
        config.setConnections(100); 
        //每服务消费者每服务每方法最大并发调用数 
        config.setActives(0); 
        return config; 
    } 
}

  参数调优可以参考以下dubbo的处理流程

  更多参数请参考官网:

  1. providerIT虾米网
  2. consumerIT虾米网

Dubbo缓存文件:

  配置服务地址的缓存,避免注册中心挂了之后对于服务通信的影响,客户端做以下配置 :

spring.application.name=springboot-dubbo-client 
server.port=8889 
 
# nacos 注册中心 
dubbo.registries.wenzhou.address=nacos://localhost:8848 
# 配置服务地址的缓存,避免注册中心挂了之后对于服务通信的影响 
dubbo.registries.wenzhou.file=${user.home}/dubbo.cache

  然后启动服务提供者/服务消费者,可以到用户目录下看到一个文件,dubbo.cache

 

  然后我们打开它:

  我们会发现服务信息已经被缓存下来了。这个时候,把注册中心关了,再去访问接口 ,发现也是没问题的。

  更多特性请参考官网


发布评论
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

dubbo(2.5.3)源码之Directory与LoadBalance知识解答
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。