重拾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入门
分布式系统
Web应用的演进
随着互联网的发展,Web应用的规模不断扩大,其发展经历了以下阶段
-
单一应用架构 ,一个应用,将所有功能都部署在一起。
-
垂直应用架构 ,将应用拆成互不相干的几个应用
-
分布式服务架构 ,当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。 此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
-
流动计算架构 ,当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。 此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
什么是分布式系统
分布式系统是若干个独立运行的系统的集合,使得用户使用起来像是在用一个系统。
Dubbo是什么
一款高性能、轻量级的开源 Java RPC 分布式服务框架。它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。RPC一般指远程过程调用。RPC是远程过程调用(Remote Procedure Call)的缩写形式
Dubbo能做什么
-
当服务越来越多时,服务 URL 配置管理变得非常困难,F5 硬件负载均衡器的单点压力也越来越大。 -
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。 -
服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?
Dubbo架构
模块 | 描述 |
---|---|
Provider | 服务提供者 |
Consumer | 服务消费者 |
Registry | 服务注册中心 |
Monitor | 服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调度过程
-
0.start : 服务容器负责启动,加载,运行服务提供者。
-
1.register : 服务提供者在启动时,向注册中心注册自己提供的服务。
-
2.subscribe:服务消费者在启动时,向注册中心订阅自己所需的服务。
-
-
-
5.count: 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo 整合SpringBoot 入门
以Zookeeper为注册中心,首先要安装并启动Zookeeper
项目结构
服务者和消费者共同依赖的接口(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
配置覆盖关系
负载均衡
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 连接模型. 客户端,服务端