目录
1.什么是Dubbo
dubbo:是一个基于soa思想的rpc框架(rpc:远程服务调用)
1.高性能RPC框架
2.服务治理 资源调度
dubbo是一个分布式服务框架,致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
soa思想:面向服务的架构
给每一个模块暴露对应的ip和端口,当做一个服务进行运行
重点在于服务的管理(负载均衡,容灾模式,服务的横向扩展)
2.架构的演变
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。 垂直应用架构 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。 分布式服务架构 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。 流动计算架构(自认为这个是微服务) 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
分布式服务架构:
3.dubbo的架构图(链接)
节点 | 角色说明 |
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器,建议是spring |
1.dubbo的demo(链接)
1.服务提供者
1.引入依赖 spring dubbo zookeeper zkclient
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.12</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
2.开发服务类(接口和实现类)
public interface DemoService {
String sayHello(String name);
}
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
System.out.println("hello"+name);
return "hello"+name;
}
}
3.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://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 唯一应用名,标识(项目模块名)-->
<dubbo:application name="dubbo_provider1" />
<!-- 使用multicast广播注册中心暴露服务地址multicast://224.5.6.7:1234 指定注册中心(zookeeper)-->
<dubbo:registry address="zookeeper://192.168.231.135:2181" />
<!-- 用dubbo协议在20880端口暴露服务 指定服务的协议:dubbo协议(固定)和使用的端口号(随意)-->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 注册服务到注册中心-->
<dubbo:service interface="com.liziyi.service.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 服务实现类-->
<bean id="demoService" class="com.liziyi.service.impl.DemoServiceImpl" />
</beans>
4.发布服务
public class Provider {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
context.start();
System.out.println("服务提供者,开始提供服务----------------");
System.in.read(); // 按任意键退出
}
}
2.创建服务消费者
1.引入依赖 spring dubbo zookeeper zkclient
2.调用服务配置服务
1.将生产者接口拷贝|创建 到消费者中
public interface DemoService {
String sayHello(String name);
}
3.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://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 服务消费者-->
<dubbo:application name="dubbo_consumer1" />
<!-- 使用multicast广播注册中心暴露发现服务地址 配置注册中心(zookeeper)-->
<dubbo:registry address="zookeeper://192.168.231.135:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService 调用服务-->
<dubbo:reference id="demoService" interface="com.liziyi.service.DemoService" />
</beans>
3.服务调用
public class Consumer {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
context.start();
DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
String hello = demoService.sayHello("world"); // 执行远程方法
System.out.println( hello ); // 显示调用结果
}
}
3.连接zookeeper(服务注册与发现的注册中心)
cd /opt/software/zookeeper/bin
#连接客户端
./zkCli.sh
这就证明连接成功!
查看里面的内容
dubbo%3A%2F%2F192.168.231.1%3A20880%2Fcom.liziyi.service.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo_provider1%26dubbo%3D2.5.3%26interface%3Dcom.liziyi.service.DemoService%26methods%3DsayHel%3D504%26side%3Dprovider%26timestamp%3D1626694429185
转码:
public class TestDubborProviders {
@Test
public void test() throws UnsupportedEncodingException {
String s = "dubbo%3A%2F%2F192.168.231.1%3A20880%2Fcom.liziyi.service.DemoService%3Fanyhost%3Dtrue%26application%" +
"3Ddubbo_provider1%26dubbo%3D2.5.3%26interface%3Dcom.liziyi.service.DemoService%26methods%3DsayHel%3D504" +
"%26side%3Dprovider%26timestamp%3D1626694429185";
String decode = URLDecoder.decode(s, "UTF-8");
System.out.println(decode);
}
}
结果:
dubbo://192.168.231.1:20880/com.liziyi.service.DemoService?anyhost=true&application=dubbo_provider1&dubbo=2.5.3&interface=com.liziyi.service.DemoService&methods=sayHel=504&side=provider×tamp=1626694429185
2.dubbo三种开发方式
1.dubbo+spring 上面的demo
2.dubbo 原始API
3.dubbo+spring 注解式开发
3.dubbo的配置优先级
设置过期时间:(链接)
方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。
(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。
4.属性配置(🔗)
5.注解配置(🔗)
使用@Service就不需要在provider.xml文件中指定下面这几条内容:
@Service
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayHello(String name) {
return "annotation: hello, " + name;
}
}
4.dubbo用法
1.启动时检查(🔗)
2.集群容错(🔗)
1.服务的集群容错:
1.搭建服务方 3个或多个服务
做集群的时候,复制duboo_provider1后如果出现下面情况:
rename改名即可
2.启动多个服务
因为在一台机器上,所以配置三个端口 20881 ,2, 3
3.集群负载均衡策略(🔗)
建议配置在服务端
Random LoadBalance 随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。 RoundRobin LoadBalance 轮询,按公约后的权重设置轮询比率。 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 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" /> 配
一致性hash算法
因为热点问题,提出虚拟节点。
默认160份,均匀分布在圆上,找到某个虚拟节点,其实就是对应的真实节点。
真实节点:
192.168.0.3
虚拟:1.1.1.0-1.1.1.159(举例)
192.168.0.4
虚拟:2.1.1.0-2.1.1.159
192.168.0.5
虚拟:3.1.1.0-3.1.1.159
4.集群容错(🔗)
5.线程模型(🔗)
如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。
但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。
如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。
参数详情见官网
6.直连提供者(🔗)
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
用法:在消费端
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
7.多协议(链接)
1.dubbo协议(链接)
Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
2.rmi协议
适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。 适用场景:常规远程服务方法调用,与原生RMI服务互操作
8.多注册中心(🔗)
Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。另外,注册中心是支持自定义扩展的
9.服务分组(🔗)
10.JSON泛化调用(🔗)
对于Dubbo泛化调用,提供一种新的方式:直接传递字符串来完成一次调用。即用户可以直接传递参数对象的json字符串来完成一次Dubbo泛化调用
当客户端没有接口时,怎么调用服务端的服务呢?
客户端的consumer.xml添加上generic="true"
<dubbo:reference id="demoService" interface="com.liziyi.service.DemoService" generic="true" />
客户端
public class Consumer {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
GenericService genericService = (GenericService) context.getBean("demoService");
// 传递参数对象的json字符串进行一次调用
// 参数1:调用的方法名称
// 参数2:调用参数类型
// 参数3:调用参数类型对应的值
Object res = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"xiaohei"});
System.out.println("result[setUser]:"+res); // 响应结果:result[setUser]:{name=Tom, class=com.xxx.api.service.User, age=24}
System.in.read(); // 按任意键退出
}
}
11.服务化最佳实践(🔗)
版本:
每个接口都应定义版本号,为后续不兼容升级提供可能,如: <dubbo:service interface="com.xxx.XxxService" version="1.0" />。 建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。 当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。
调用
Provider 端需要对输入参数进行校验。如有性能上的考虑,服务实现者可以考虑在 API 包上加上服务 Stub 类来完成检验。如下参数验证:
参数验证(🔗)
5.springboot与dubbo整合
1.springboot与dubbo的依赖
<!--dubbo-springBoot依赖 springboot是1.5.7只能用dubbo的1.0.2 1.5.6对应1.0.1 2.1.5对应1.1.3-->
<dependency>
<groupId>com.gitee.reger</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--整合web开发-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--testclient-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
2.建表
3.拆分模块
cmfz_user cmfz_commons cmfz_book cmfz_back_sys(后台) cmfz_front_sys(前台)
4.开发用户服务
这样子模块可以拿到父模块全部的依赖。
1).业务层加入下面第三个注解
@Service
@Transactional
@com.alibaba.dubbo.config.annotation.Service(interfaceClass = UserService.class)
public class UserServiceImpl implements UserService {
2).配置文件中配置dubbo
server:
port: 8989
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
data-username: root
data-password: 123456
driver-class-name: com.mysql.jdbc.Driver
dubbo:
application:
name: cmfz_user
registry:
address: zookeeper://192.168.231.136:2181
protocol:
name: dubbo
port: 20881
base-package: com.liziyi.cmfz.service
mybatis:
mapper-locations: classpath:com/liziyi/mapper/*.xml