1、Dubbo基础知识
1、什么是分布式系统?
分布式系统是若干个独立计算机的集合,这些计算机对于用户来说就像单个相关系统。分布式系统是建立在网络之上的软件系统。
2、分布式的发展演练
3、RPC
简单的理解就是,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
RPC两个核心模块:通讯,序列化
2、Dubbo
Dubbo是一款高性能、轻量级的开源RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
基本概念:
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己 提供的服务。
服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
下面罗列一些Dubbo常用的,也就是说每个项目的Dubbo的Xml文件中基本都会出现的标签,并以表格的形式列举标签中常见的可用属性:
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.test.UserServiceBo" ref="userService" version="1.0.0" timeout="3000"/>
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="userService" interface="com.test.UserServiceBo" version="1.0.0" timeout="3000"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
Dubbo配置的覆盖规则:
- 方法配置级别优于接口级别
- Consumer端配置优于Provider配置优于全局配置
- 最后是Dubbo Hard Code的配置值
2.1、使用dubbo+zookeeper整合springboot创建提供者、消费者案列:
提供者:
首先导入相关的依赖jar包
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<!--<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
这里有个版本配合,dubbo的版本在25之间使用zkClient的jar包,2.5之后使用curator依赖包。
然后在properties中配置
#要注册的服务名
dubbo.application.name=provider-server
#注册中心的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描包下要被注册的服务
dubbo.scan.base-packages=com.hao.service
编写实现类
@Service //注意:这里使用的是dubbo下的service注解
@Component //让其被扫描到,放到IOC容器中去
public class BuyTicketImpl implements BuyTicket {
@Override
public String buyTicket() {
return "快点买票啊,不然就回不去了";
}
}
这样就可以在监控中心可以看到该提供者了
消费者:
首先跟提供者一样导入一样的jar包,跟上面一样
其次就在配置文件中配置消费者信息
#消费者名字
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
然后就是使用了,首先先创建一个跟提供者一样包路径的接口,再生成相应的实现类
@Controller
@Service //注意:这里使用的是springmvc中的service注解
public class UserService {
@Reference
BuyTicket buyTicket;
@RequestMapping("/dd")
@ResponseBody
public String buyTicket(){
String s = buyTicket.buyTicket();
return s;
}
}
这样消费者就可以通过dubbo+zookeeper拿到不同服务器上的服务了。这个是基于springboot做的。
下面是基于spring项目搭建的
提供者:
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<?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:context="http://www.springframework.org/schema/context"
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://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="DubboDemo-Provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<bean id="UserServiceImpl" class="com.hao.service.UserServiceImpl"/>
<dubbo:service interface="com.hao.service.UserService" ref="UserServiceImpl"/>
</beans>
//提供者启动类
public class test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-provider.xml"); //通过读取配置文件获取容器
context.start();
System.out.println("提供者服务已注册成功");
try {
System.in.read(); //让该程序一直执行下去,不会停止
} catch (IOException e) {
e.printStackTrace();
}
}
}
在启动的时候可能会报日志冲突的问题:需要自己在resource文件夹下添加log4j.properties文件
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
消费者:
消费者这里也要创建一个和提供者一样的接口。
public interface UserService {
String say();
}
配置文件
<?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"
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">
<dubbo:application name="dubboDemo-consumer"/>
<!-- 使用预发 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference interface="com.hao.service.UserService" id="user"/>
</beans>
public class test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-consumer.xml"); //也是通过读取配置文件获取容器,再通过容器获取指定的接口实现类
context.start();
UserService user = context.getBean("user", UserService.class);
System.out.println("执行前");
String say = user.say();
System.out.println(say); //这里注意:消费者只能获取到提供者接口实现类中return的值,方法具体中具体的实现获取不了。
System.out.println("执行后");
try {
System.in.read(); //让程序一直运行下去。
} catch (IOException e) {
e.printStackTrace();
}
}
}
zookeeper宕机与dubbo直连
现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务。
原因:
- 监控中心宕机后不影响使用,只是丢失部分采样数据。
- 数据库宕机后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务。
- 注册中心对等集群,任意一台宕机后,将自动切换到另一台。
- 注册中心全部宕机后,服务提供者和服务消费者仍能通过本地缓存通讯。
- 服务提供者无状态,任意一台宕机后,不影响使用。
- 服务提供者全部宕机后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。
集群下dubbo负载均衡配置
在集群负载均衡是,dubbo提供了多种均衡策略,默认为random随机调用。
负载均衡策略:Random(随机)、RoundRobin(循环)、LeastActive(最少调用)、ConsistentHash(一致性)
服务降级
当服务器压力剧增的情况下,根据实际业务员情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
其中:
- mock=force:return+null表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
- mock=fail:return+null表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
集群容错模式
Failover Cluster:失败自动切换,当出现失败时,重试其它服务器。
Failfast Cluster:快速失败,只发起一次调用,失败立即报错。
Failback Cluster:失败自动恢复,后代记录失败请求,定时重发。
Forking Cluster:并行调用多个服务器,只要一个成功即返回。
Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。
3、Dubbo原理
RPC原理
一次完整的RPC调用流程(同步调用,异步另说)如下:
1)服务消费方(client)调用以本地调用方式调用服务;
2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3)client stub找到服务地址,并将消息发送到服务端;
4)server stub收到消息后进行解码;
5)server stub根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给server stub;
7)server stub将返回结果打包成消息并发送至消费方;
8)client stub接收到消息,并进行解码;
9)服务消费方得到最终结果。
RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。
Netty通信原理
Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。
Selector 一般称 为选择器 ,也可以翻译为 多路复用器,Connect(连接就绪)、Accept(接受就绪)、Read(读就绪)、Write(写就绪)
Netty基本原理
Dubbo原理(框架设计)
- config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
- proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
- monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
- protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
- exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient,ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
dubbo原理(启动解析、加载配置信息)
dubbo原理(服务暴露)
dubbo原理(服务引用)
dubbo原理(服务调用)