Dubbo学习内容
Dubbo发展背景
Dubbo概述
RPC
Zookeeper、Dubbo控制台的安装
Dubbo入门例子
Dubbo详细讲解
Dubbo整合spring mvc
Dubbo整合springboot
Dubbo发音:|ˈdʌbəʊ|
Dubbo官方网站:http://dubbo.apache.org/
Dubbo是阿里巴巴开发的,已经贡献给Apache,并且已经成Apache的顶级开源项目
1. Dubbo的发展背景
文档:http://dubbo.apache.org/zh-cn/docs/user/preface/background.html
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
1.1 单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
使用一个web容器(如tomcat),然后使用Servlet/JSP技术,最后选择一个合适数据库管理系统来存储数据(MySQL、Oracle)。
1.2 垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用
,以提升效率。
此时,用于加速前端页面开发的Web框架(MVC)是关键。
用户系统、权限系统、商品系统、订单系统、物流系统…
特点:系统独立部署,每一个系统都有完整的前后端;
问题:各个系统无法做到完全独立,公共模块无法复用,系统之间通信比较麻烦;
1.3 分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,这时将核心业务抽取出来,作为独立的服务
,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
分布式架构的难点:
- 各个系统如何进行远程调用
- 如何进行业务拆分
1.4 流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
2. RPC
RPC(Remote Procedure Call)- 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层的网络技术协议。RPC假定某种传输协议的存在(如TCP),为通信程序携带数据;
通俗的说,RPC可以让我们像调用本地方法一样调用远程计算机提供的服务;
2.1 RPC的简单原理
1. 客户端以本地调用的方式调用远程服务
2. client stub接收到调用后,将方法、参数等组装成能够进行网络传输的消息;
3. client stub查找服务地址,找到之后,将消息发送到服务端;
4. server stub收到消息之后,对收到的消息进行解码;
5. server stub根据解码结果,使用反射的方式调用本地服务;
6. 服务端执行完成之后将结果返回给Server stub;
7. server stub将返回结果打包成消息并发送给客户端;
8. client stub收到消息后,对结果进行解码;
总结: 实现RPC的三个技术点
- 服务寻址
- 网络连接
- 数据的序列化
3. Dubbo
3.1 Dubbo是什么
Apache Dubbo™ 是一款高性能Java RPC框架
。
Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用
,智能容错和负载均衡
,以及服务自动注册和发现
。
3.2 Dubbo作者
从左至右:刘超,梁飞,闾刚,陈雷,刘昊旻,李鼎
3.3 Dubbo的发展历程
2008年阿里内部开始使用;
2009年初,发布1.0版本;
2010年初,发布2.0版本;
2011年10月27日,阿里将Dubbo开源,版本号为2.0.7;
2012年3月,发布2.1.0版本;
2014年10月,发布2.3.11版本,之后版本停滞;
2017年9月,阿里重启维护,重点升级了依赖的JDK版本,发布2.5.4/2.5.5版本;
2017年10月,发布2.5.6版本;
2017年11月,发布2.5.7版本,后期集成了SpringBoot;
2014年10月的时候,当当网Fork了Dubbo源代码,在此基础上增加了HTTP REST协议,发布版本号2.8.0,名字叫DubboX
;
网易考拉在Dubbo基础上开发了DubboK;
3.4 Dubbo架构
-
架构中的角色
Provider:服务提供者
Consumer:服务消费者
Registry:服务注册和发现的中心
Monitor:监控中心,用于统计服务调用情况
Container:Dubbo容器
-
调用关系
0、容器负责启动、加载、运行服务提供者、消费者;
1、服务提供者在启动时,向注册中心注册自己提供的服务;
2、服务消费者在启动时,向注册中心订阅自己需要的服务;
3、注册中心返回服务提供者的地址列表给消费者;
如果有服务变更(服务的上线或下线),注册中心会基于长连接的方式推送变更给消费者;
4、服务消费者从地址列表中,基于
软件负载均衡算法
,选择一个服务提供者进行调用,如果调用失败,可以重试其它提供者;5、服务消费者和提供者,在内存中累计调用时间和调用次数,每分钟向监控中心发送一次统计数据;
4. 安装单机Zookeeper
4.1 windows版本
-
解压zookeeper-3.4.12.tar.gz到当前目录
-
在zookeeper-3.4.12目录下创建
data
目录
- 将
zookeeper-3.4.12/conf
目录下的zoo_sample.cfg
文件重命名为zoo.cfg
- 修改
zoo.cfg
文件中的dataDir
选项
-
启动zookeeper服务端
双击
zookeeper-3.4.12/bin/zkServer.cmd
-
启动zookeeper的客户端
双击
zookeeper-3.4.12/bin/zkCli.cmd
4.2 Linux安装
-
将zookeeper-3.4.12.tar.gz上传到centos的/opt目录下
-
解压zookeeper-3.4.12.tar.gz到当前文件夹下(即/opt)
tar -zxf zookeeper-3.4.12.tar.gz -C /opt
-
进入zookeeper-3.4.12,创建一个data目录
cd zookeeper-3.4.12 mkdir data
-
进入zookeeper-3.4.12/conf目录,将zoo_sample.cfg修改zoo.cfg
cd conf mv zoo_sample.cfg zoo.cfg
-
编辑zoo.cfg文件
# 将dataDir修改为../data dataDir=../data
-
启动ZK server
cd bin ./zkServer.sh start
-
启动ZK客户端
./zkCli.sh
5. Dubbo控制台安装
-
Dubbo主要用于服务治理
-
可以通过控制台查看所有服务(提供者、消费者)
-
可以设置负载均衡、权重调节、服务降级策略等
Dubbo控制台默认访问地址:http://localhost:7001
Dubbo默认的用户名/密码:root/root
在dubbo-admin.jar所在目录执行 java -jar dubbo-admin.jar
即可(命令行执行)
6. Dubbo入门工程 - Hello World
-
maven依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.5</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.13.0</version> </dependency>
-
工程搭建 - maven聚合工程
dubbo-hello dubbo-hello-interface dubbo-hello-provider dubbo-hello-consumer
-
步骤
- 在interface工程创建服务接口
- 在provider工程实现接口
- 在provider工程发布服务
- 在consumer工程中引用
- 在consumer工程中调用远程服务
Demo
-
首先创建一个聚合工程
其中consumer与provider都需要依赖interface
-
在interface工程创建服务接口 HelloService
public interface HelloService { String hello (String name); }
-
在provider工程实现接口 HelloServiceImpl
public class HelloServiceImpl implements HelloService { @Override public String hello(String name) { System.out.println("hello invoke!"); return "hello" + name; } }
-
在provider工程发布服务
provider.xml<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!--1.应用名称--> <dubbo:application name="hello-service"/> <!--2.注册中心:把dubbo服务注册到注册中心--> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!--3.使用dubbo协议在20880端口号发布服务--> <dubbo:protocol name="dubbo" port="20880" /> <!--4.暴露服务--> <dubbo:service ref="helloService" interface="com.service.HelloService"> </dubbo:service> <!--5.配置服务实现--> <bean id="helloService" class="com.service.impl.HelloServiceImpl" /> </beans>
ProviderTest
public class ProviderTest {
public static void main(String[] args) throws IOException {
new ClassPathXmlApplicationContext("provider.xml");
System.in.read();
}
}
- 在consumer工程中引用
consumer.xml
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--1.应用名称-->
<dubbo:application name="hello-consumer"/>
<!--2.配置注册中心-->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!--3.引用服务-->
<!--创建了HelloService的代理对象 并注册成BEAN-->
<dubbo:reference id="helloService" interface="com.etoak.service.HelloService">
</dubbo:reference>
</beans>
- 在consumer工程中调用远程服务
ConsumerTest
public class ConsumerTest {
public static void main(String[] args) throws IOException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("consumer.xml");
//获取远程服务HelloService的代理对象
HelloService helloService = ioc.getBean(HelloService.class);
//调用远程服务
String result = helloService.hello("Dubbo");
System.out.println(result);
System.in.read();
}
}
7. Dubbo详解
7.1 Dubbo的注册中心有哪些
1、Multicast注册中心
Multicast注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。
2、Zookeeper注册中心
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,
支持变更推送
,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。3、Redis注册中心
4、Simple 注册中心
5、Nacos注册中心
推荐使用 zookeeper注册中心
7.2 Dubbo支持的协议
dubbo、rmi、hessian、http、webservice、rest、thrift、memcached、redis、grpc
推荐使用 Dubbo 协议
7.3 Dubbo服务在ZK中的存储结构
7.4 启动时检查
Dubbo默认情况下会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"
。
7.4.1 关闭单个服务的启动检查 - 消费方
可以通过<dubbo:reference check="false"
> 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动;
<dubbo:reference id="helloService" check="false"
interface="com.etoak.service.HelloService">
</dubbo:reference>
7.4.2 关闭所有服务的启动检查 - 消费方
<dubbo:consumer check="false" />
7.4.1 关闭注册中的启动检查 - 消费方和提供方
<dubbo:registry address="zookeeper://127.0.0.1:2181"
check="false" />
7.5 配置加载流程
文档地址
此篇文档主要讲在应用启动阶段,Dubbo框架如何将所需要的配置采集起来(包括应用配置、注册中心配置、服务配置等),以完成服务的暴露和引用流程。
7.5.1 配置来源
-
JVM System Properties,-D参数
-
Externalized Configuration,外部化配置 -
ServiceConfig、ReferenceConfig等编程接口采集的配置、spring配置
-
本地配置文件
dubbo.properties
(写在src/main/resources)配置在classpath根目录
7.5.2 配置流程
配置加载的优先级
jvm参数 > 外部化配置 > 编程接口、spring配置 > dubbo.properties
Idea中配置jvm参数
7.6 XML配置 - 不同粒度配置的覆盖关系
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
-
配置级别
方法级:
<dubbo:method>
接口级:
<dubbo:service>
、<dubbo:reference>
全局级:
<dubbo:provider>
、<dubbo:consumer>
-
配置优先级
7.7 直连提供者
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连;点对点直连方式,以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
<dubbo:reference id="helloService"
url="dubbo://127.0.0.1:20880"
interface="com.etoak.service.HelloService">
<dubbo:method name="hello" />
</dubbo:reference>
7.8 本地存根
提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等。
实现步骤
-
在服务接口旁边写一个名字为
接口名Stub
的服务实现类 -
在这个实现类中添加一个有参的构造方法,构造方法参数就是这个服务接口的代理(也就是可以直接写这个服务接口)
-
配置服务
<!--这种方式的配置需要命名十分规范 会去找相同文件夹下的serviceNameStub 即:com.etoak.service.HelloServiceStub--> <dubbo:service interface="com.service.HelloService" stub="true" />
<!-- 直接指定stub方式 --> <dubbo:service interface="com.service.HelloService" stub="com.etoak.service.HelloServiceStub" />
-
Demo
/** * 这个代码是由提供者边写 消费者执行 */ public class HelloServiceStub implements HelloService{ HelloService helloService; //构造方法参数:是真正的远程代理对象 public HelloServiceStub(HelloService helloService){ this.helloService = helloService; } @Override public String hello(String name) { System.out.println("这行代码在客户端(消费方)执行"); //如果name不为空 if(name != null && ! "".equals(name)){ //发起远程调用 return helloService.hello(name); } //如果为空 则不发起远程调用 return "参数异常"; } }
7.9 服务分组
当一个接口有多种实现时,可以用 group 区分。
-
提供方配置
<dubbo:service ref="helloService" group="service" interface="com.etoak.service.HelloService"> <dubbo:method name="hello" timeout="4000" /> </dubbo:service> <bean id="helloService" class="com.etoak.service.impl.HelloServiceImpl" /> <dubbo:service ref="helloService2" group="service2" interface="com.etoak.service.HelloService"> </dubbo:service> <bean id="helloService2" class="com.etoak.service.impl.HelloService2" />
-
消费方配置
<dubbo:reference id="helloService" group="service2" interface="com.etoak.service.HelloService"> <dubbo:method name="hello" /> </dubbo:reference>
7.10 服务的多版本
当一个接口实现出现不兼容需要升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
服务端配置方式
<dubbo:service ref="helloService" stub="true"
timeout="6000" retries="5"
version="1.0"
interface="com.etoak.service.HelloService">
</dubbo:service>
消费端配置
<dubbo:reference id="helloService"
timeout="5500"
version="2.0"
interface="com.etoak.service.HelloService">
</dubbo:reference>
7.11 负载均衡
Dubbo 提供了多种均衡策略,缺省为 random
随机调用。
1. Random LoadBalance
随机
,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
2. RoundRobin LoadBalance
轮询,按公约后的权重
设置轮询比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
3. LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差
。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
4. ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者
。
当某一台提供者宕机时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
默认只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />
默认用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />
控制台配置
7.12 服务降级
可以通过服务降级
功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。- 还可以改为
mock=fail:return+null
表示消费方对该服务的方法调用失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
8. Dubbo整合springboot
用户服务 - 根据id查询用户信息
-
创建工程
dubbo-boot dubbo-boot-bean java bean dubbo-boot-interface 服务接口 dubbo-boot-mapper mapper接口 + sql dubbo-boot-provider 服务实现 springboot dubbo-boot-consumer 消费者 springboot
-
编写步骤
-
在bean工程创建User,要实现序列化接口
-
在mapper工程中创建UserMapper接口、sql
-
在interface工程定义服务接口UserService
-
在provider工程编写服务实现
整合mybatis、dubbo
-
在consumer工程引用UserService服务
整合dubbo
-
启动springboot测试
先启动注册中心
再启动服务提供者
再启动消费者
-
Demo
在bean创建User实现序列化接口
@Data
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
private String email;
}
mapper中创建UserMapper接口&xml
public interface UserMapper {
User getById(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.UserMapper">
<select id="getById" parameterType="int" resultType="user">
Select id,name,age,email from sys_user where id =#{value}
</select>
</mapper>
在interface工程定义服务接口UserService
public interface UserService {
User getById(int id);
}
在provider工程编写服务实现&配置&启动类
//com.alibaba 的 service
@Service(timeout = 6000 ,retries = 3)
public class UserServiceImpl implements UserService {
@Autowired
UserMapper usermapper;
@Override
public User getById(int id) {
User user = usermapper.getById(id);
return user;
}
}
配置文件application.yml
server:
port: 9090
#数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1/et2004?serverTimezone=UTC
username: root
password: etoak
#mybatis
mybatis:
type-aliases-package: com.etoak.bean
mapper-locations: classpath:mappers/*.xml
#pageHelper
pagehelper:
reasonable: true
#dubbo
dubbo:
application:
name: user-service
registry:
address: zookeeper://127.0.0.1:2181
protocol:
name: dubbo
port: 20880
启动类UserApp
@SpringBootApplication
@EnableDubbo
@MapperScan(basePackages = "com.etoak.mapper")
public class UserApp {
public static void main(String[] args) {
SpringApplication.run(UserApp.class,args);
}
}
在consumer工程引用UserService服务整合dubbo
@SpringBootApplication
@EnableDubbo
@RestController
public class ConsumerApp {
@Reference
UserService userService;
@GetMapping("/user/{id}")
public User getUser(@PathVariable int id){
User byId = userService.getById(id);
return byId;
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
application.yml
server:
port: 8080
dubbo:
application:
name: user-consumer
registry:
address: zookeeper://127.0.0.1:2181