Dubbo 什么是Dubbo Dubbo是阿里巴巴公司开源的一个高性能、轻量级的 Java RPC 框架
致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。
Dubbo主要特性
面向接口代理的高性能RPC调用:提供高性能的基于代理的远程调用能力,服务以接口为粒度,屏蔽了远程调用底层细节。
智能负载均衡:内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。
服务自动注册与发现:支持多种注册中心服务,服务实例上下线实时感知。
高度可扩展能力:遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。
运行期流量调度:内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。
可视化的服务治理与运维:提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。
Spring Cloud 团队
Spring Cloud Alibaba
HTTP 短连接 重新建立TCP连接
RPC Dubbo Spring Cloud
Dubbo的基本使用 先来个例子体验一下:这个例子中没有使用注册中心
创建两个maven工程一个当server一个当client,利用quickstart模板,然后在server端起一个api模块去专门的存放接口,然后再创建另一个模块存放服务端应该有的服务
那么这时候在api里面编写接口,在server里面写具体的业务实现类实现api里面的接口,那么在同个工程不同模块里面如何在server端导入api的接口呢?利用pom里面的依赖可以导入api依赖从而实现。
api里面定义接口
1 2 3 public interface IloginService { String login (String username, String password) ; }
然后在服务端写实现类,并且在服务端写好配置类,配置dubbo以及定义对外的服务名以及服务对应的实现类
服务端的实现类
1 2 3 4 5 6 7 8 9 10 11 public class IloginServiceImpl implements IloginService { @Override public String login (String username, String password) { if (username.equals("admin" ) && password.equals("admin" )){ return "success" ; } return "failed" ; } }
server的配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://dubbo.apache.org/schema/dubbo" xmlns ="http://www.springframework.org/schema/beans" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <dubbo:application name ="dubbo-server" /> <dubbo:registry address ="N/A" /> <dubbo:protocol name ="dubbo" port ="20880" /> <dubbo:service interface ="com.bitzh.dubbo.server.IloginService" ref ="loginService" /> <bean id ="loginService" class ="com.bitzh.dubbo.server.IloginServiceImpl" /> </beans >
然后再server的启动类启动dobbo的服务,启动服务端记住duboo的url以供客户端访问
1 2 3 4 5 6 7 8 public class App { public static void main ( String[] args ) { Main.main(args); } }
这个是server的pom依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <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 > <parent > <groupId > com.bitzh.server</groupId > <artifactId > dubbo-server-demo</artifactId > <version > 1.0-SNAPSHOT</version > </parent > <artifactId > dubbo-server</artifactId > <packaging > jar</packaging > <name > dubbo-server</name > <url > http://maven.apache.org</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.apache.dubbo</groupId > <artifactId > dubbo</artifactId > <version > 2.7.8</version > </dependency > <dependency > <groupId > com.bitzh.server</groupId > <artifactId > dubbo-server-api</artifactId > <version > 1.0-SNAPSHOT</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 3.8.1</version > <scope > test</scope > </dependency > </dependencies > </project >
现在问题是不同工程如何进行通信呢?他是跨进程的
那么很显然第一件事情要在client端引入整个server端的依赖,把server工程打包然后再client中导入api依赖
执行install将项目的构建结果安装到本地 Maven 仓库中,以供其他项目使用
然后在client端里面导入模块的api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <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.bitzh.dubbo.client</groupId > <artifactId > dubbo-client-demo</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > jar</packaging > <name > dubbo-client-demo</name > <url > http://maven.apache.org</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.apache.dubbo</groupId > <artifactId > dubbo</artifactId > <version > 2.7.8</version > </dependency > <dependency > <groupId > com.bitzh.server</groupId > <artifactId > dubbo-server-api</artifactId > <version > 1.0-SNAPSHOT</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 3.8.1</version > <scope > test</scope > </dependency > </dependencies > </project >
然后再客户端中实现逻辑
然后我们要将两个工程进行通信利用Dubbo,那么两个客户端和服务端都要引入Dubbo的依赖(注意不是两个工程)
然后client端写dubbo配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://dubbo.apache.org/schema/dubbo" xmlns ="http://www.springframework.org/schema/beans" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <dubbo:application name ="dubbo-client" /> <dubbo:registry address ="N/A" /> <dubbo:reference id ="loginService" interface ="com.bitzh.dubbo.server.IloginService" url ="dubbo://192.168.10.1:20880/com.bitzh.dubbo.server.IloginService" /> </beans >
client启动类
1 2 3 4 5 6 7 8 9 10 11 public class App { public static void main ( String[] args ) { IloginService iloginService = null ; ApplicationContext ac = new ClassPathXmlApplicationContext ("classpath:/META-INF/spring/application.xml" ); iloginService = ac.getBean(IloginService.class); System.out.println( iloginService.login("admin" , "admin" ) ); } }
Dubbo支持的注册中心 consul
zookeeper
Eureka
Redis
etcd
nacos
nacos做注册中心 从github上拉取nacos的源码方式获取nacos1.1.4版本的压缩包和源码,解压之后打开,然后打开nacos的confgi下面有nacos-mysql.sql然后在数据库中执行脚本就会生成表,
在application.properties中配置
然后在bin里面的startup.cmd里面配置MODE为standalone
配置完成之后,回到nacos然后再bin目录下启动nacos
或者在nacos-server里面启动要先配置vm
然后访问控制台http://localhost:8848/nacos/#/login
账号密码都是nacos
然后回到之前的server项目里面配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://dubbo.apache.org/schema/dubbo" xmlns ="http://www.springframework.org/schema/beans" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <dubbo:application name ="dubbo-server" /> <dubbo:registry address ="nacos://localhost:8848" /> <dubbo:protocol name ="dubbo" port ="20880" /> <dubbo:service interface ="com.bitzh.dubbo.server.IloginService" ref ="loginService" /> <bean id ="loginService" class ="com.bitzh.dubbo.server.IloginServiceImpl" /> </beans >
然后添加依赖,添加一下日志的依赖
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-nop</artifactId > <version > 1.7.2</version > </dependency > <dependency > <groupId > com.alibaba.nacos</groupId > <artifactId > nacos-client</artifactId > <version > 1.1.4</version > </dependency >
然后服务端启动!
然后看控制台发现注册成功
然后我们如果想更改别的注册中心更改一下配置和端口就可以了
拔插式的服务
1 <dubbo:registry address ="nacos://localhost:8848" />
配置一下客户端,导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://dubbo.apache.org/schema/dubbo" xmlns ="http://www.springframework.org/schema/beans" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <dubbo:application name ="dubbo-client" /> <dubbo:registry address ="nacos://localhost:8848" timeout ="100000" /> <dubbo:reference id ="loginService" interface ="com.bitzh.dubbo.server.IloginService" url ="dubbo://192.168.10.1:20880/com.bitzh.dubbo.server.IloginService" /> </beans >
然后启动客户端就完成了注册
Dubbo Spring Cloud
功能组件
Spring Cloud
Dubbo Spring Cloud
分布式配置(Distributed configuration)
Git、Zookeeper、Consul、JDBC
Spring Cloud 分布式配置 + Dubbo 配置中心
服务注册与发现(Service registration and discovery)
Eureka、Zookeeper、Consul
Spring Cloud 原生注册中心 + Dubbo 原生注册中心
负载均衡(Load balancing)
Ribbon(随机、轮询等算法)
Dubbo 内建实现(随机、轮询等算法 + 权重等特性)
服务熔断(Circuit Breakers)
Spring Cloud Hystrix
Spring Cloud Hystrix + Alibaba Sentinel 等
服务调用(Service-to-service calls)
Open Feign、RestTemplate
Spring Cloud 服务调用 + Dubbo @Reference
链路跟踪(Tracing)
Spring Cloud Sleuth + Zipkin
Zipkin、opentracing 等
以上对比表格摘自Dubbo Spring Cloud官方文档。
本质上 都是为业务而生
Dubbo多注册中心 多注册中心 | Apache Dubbo
Dubbo多协议 多协议 | Apache Dubbo
Dubbo 源码 怎么学?先看整体架构
了解 Dubbo 核心概念和架构 | Apache Dubbo
nacos就完成了服务注册的功能
那么如何实现注册功能呢?
首先逻辑应该是服务提供者会将ip端口号还有serversname 放到服务中心提供给消费者
那么消费者想要拿到数据必须要有对应访问的路径来调用
此时问题来了,假设需要使用注册中心的操作,如何注册到注册呢?
可以用curl的方式以post请求来进行注册,在linux环境下来执行curl(用git也可以)
从这个路径(官方文档中的案例)看出在源码nacos中肯定有个/v1/ns/instance的入口
然后继续看下去
这样就找到了目标的接口了,此时我们用的是post请求,用到的是post方法,但是在里面没有找到instance的方法而是一个controller,原因是post只是个表单的提交,表单的提交有可能只要找到目标源就ok,不一定要用路径拼接
然后在里面找到了真正的服务,打个断点
也就是说这个服务必然进入register方法,
但是每次访问这个都要写这么长的路径来进入register方法很麻烦,所以就nacos就把路径封装起来
也就是说如果我们只用传入serviceName 和URL给register就可以实现注册。
然后再源码里面已经有了很多工具来实现了,写了很多参数来封装。
那么我们新建了service然后这个service就必然要调用service.registerInstance那么我们如何实现自动注册呢?
类似于springboot的自动注册一样呢?
在配置文件中,拥有nacos服务端的地址的
我们就加了依赖和配置就完成了自动注册
springboot里面的有一个观察者模式,具体实现代码springboot里面已经实现了,写个demo来展现观察者模式
刚才我们的流程
SpringCloud去实现,在nacos我们不能增加规范,那我们就定义规范,所以去定义接口
Admin管理界面搭建 资料中把dubbo-admin-0.2.0.jar\BOOT-INF\classes 中application.properties里面注册中心的ip设置正确
使用java -jar dubbo-admin-0.2.0.jar运行即可。
注意:占用8080端口,不要冲突了。
负载均衡 集群:一个内容,部署多次,形成的整体称为集群。集群中每个个体应该部署到不同的服务器上。
伪集群:集群中内容部署到同一台服务器上,通过不同端口区分不同个体。
负载均衡是在集群前提下,当访问整个集群时,集群中每个节点被访问次数或频率的规则。
Dubbo 内置了四个负载均衡策略。默认为Random
1.内置策略 1.1Random 随机。随机访问集群中节点。访问概率和权重有关。
1.2RoundRobin 轮询。访问频率和权重有关。
权重(weight):占有比例。集群中每个项目部署的服务器的性能可能是不同,性能好的服务器权重应该高一些。
1.3LeastActive 活跃数相同的随机,不同的活跃数高的放前面。
1.4ConsistentHash 一致性Hash。相同参数请求总是发到一个提供者。
2.Provider集群 新建四个启动类。
每次启动启动类修改配置文件dubbo.protocal.port
3.设置负载均衡 3.1@Reference 调用的服务采用的负载均衡
1 2 @Reference(loadbalance = "roundrobin") private DemoDubboService demoDubboService;
3.2 @Service 当前服务采用的负载均衡算法
1 2 @Service(loadbalance = "random") public class DemoDubboServiceImpl implements DemoDubboService {
设置权重
3.3配置文件 全局设置所有provider和consumer的负载均衡效果。
1 2 3 4 5 6 7 8 9 10 11 dubbo: application: name: dubbo-provider registry: address: zookeeper://192.168.32.128:2181 protocol: port: 20884 provider: loadbalance: random consumer: loadbalance: random
完整Dubbo项目演示 1.原型 1.1部门显示 显示全部部门信息
1.2员工新增
1.3查看部门员工
2.按照分布式架构进行设计项目 设定员工管理和部门管理不在同一个模块中,需要有一个员工管理项目和一个部门管理项目。
为了方便,不去每个项目使用一个窗口,而是使用聚合项目。
3.创建数据库表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 create table dept( id int(11) primary key auto_increment, name varchar(20) ); insert into dept values(default,'开发部'); insert into dept values(default,'产品部'); create table emp( id int(11) primary key auto_increment, name varchar(20), photo varchar(200), did int(11), CONSTRAINT fk_emp_dept FOREIGN key (did) REFERENCES dept(id) );
4.创建父项目 创建项目parent。
编写pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.10.RELEASE</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.1.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.1.10.RELEASE</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> </dependencies> </dependencyManagement>
5.创建pojo项目 6.创建mapper项目 6.1编写pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependencies> <dependency> <artifactId>pojo</artifactId> <groupId>com.msb</groupId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
6.2新建配置文件 新建application-mybatis.yml
1 2 3 4 5 6 7 8 9 10 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/maven username: root password: root mybatis: mapper-locations: classpath:mybatis/*.xml type-aliases-package: com.msb.pojo
7.新建api项目 7.1编写pom.xml 1 2 3 4 5 6 7 <dependencies> <dependency> <artifactId>pojo</artifactId> <groupId>com.bjsxt</groupId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
8.新建provider 8.1编写pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <dependencies> <dependency> <artifactId>mapper</artifactId> <groupId>com.msb</groupId> <version>1.0.0</version> </dependency> <dependency> <artifactId>api</artifactId> <groupId>com.msb</groupId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> </dependency> </dependencies>
8.2新建配置文件 新建application.yml
1 2 3 4 5 6 7 8 9 10 dubbo: application: name: dubbo-provider registry: address: zookeeper://192.168.52.128:2181 # 加载其他配置文件,加载其他application-*.yml文件,多个名称之间使用逗号分隔 spring: profiles: active: mybatis
8.3新建启动类 新建com.msb.ProviderApplication
1 2 3 4 5 6 7 8 @SpringBootApplication @EnableDubbo @MapperScan("com.msb.mapper") public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class,args); } }
9.完成Dept查询功能 9.1在api中新建接口 com.msb.dubbo.service.DeptDubboService
1 2 3 public interface DeptDubboService { List<Dept> selectAll(); }
9.2在provider中新建实现类 com.msb.dubbo.service.impl.DeptDubboServiceImpl
1 2 3 4 5 6 7 8 9 @Service public class DeptDubboServiceImpl implements DeptDubboService { @Autowired private DeptMapper deptMapper; @Override public List<Dept> selectAll() { return deptMapper.selectByExample(null); } }
9.3新建项目dept 9.3.1添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies> <dependency> <artifactId>api</artifactId> <groupId>com.msb</groupId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> </dependency> </dependencies>
9.3.2编写配置文件 新建application.yml
1 2 3 4 5 dubbo: application: name: dubbo-dept-consumer registry: address: zookeeper://192.168.52.128:2181
9.3.3新建启动类 com.msb.DeptApplication
1 2 3 4 5 6 7 @SpringBootApplication @EnableDubbo public class DeptApplication { public static void main(String[] args) { SpringApplication.run(DeptApplication.class,args); } }
9.3.4新建接口及实现类 接口:com.msb.service.DeptService
实现类:com.msb.service.impl.DeptServiceImpl
1 2 3 public interface DeptService { List<Dept> showAll(); }
1 2 3 4 5 6 7 8 9 @Service public class DeptServiceImpl implements DeptService { @Reference private DeptDubboService deptDubboService; @Override public List<Dept> showAll() { return deptDubboService.selectAll(); } }
9.3.5新建控制器 com.msb.controller.DeptController
1 2 3 4 5 6 7 8 9 10 11 12 @Controller public class DeptController { @Autowired private DeptService deptService; @GetMapping("/dept") public String shwoDept(Model model){ model.addAttribute("list",deptService.showAll()); return "dept"; } }
9.3.6 新建页面 在resources /templates新建dept.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1" width="500"> <tr> <th>编号</th> <th>部门名称</th> <th>查看</th> </tr> <tr th:each="dept : ${list}"> <td th:text="${dept.id}"></td> <td th:text="${dept.name}"></td> <td> <a th:href="@{/showEmp(did=${dept.id})}">查看</a> </td> </tr> </table> </body> </html>