基础知识
分布式基础理论
- 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统;
- 分布式系统(distributed system)是建立在网络之上的软件系统。
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
应用框架发展演变
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。
缺点: 1、性能扩展比较难;
2、协同开发问题;
3、不利于升级维护;垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
- 通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。
-
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC:远程过程调用)是关键。流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键。RPC
什么是RPC
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
RPC基本原理
dubbo核心概念
简介
Apache Dubbo是一款高性能、轻量级的开源
Java RPC
框架,它提供了三大核心能力: 面向接口的远程方法调用,
- 智能容错和负载均衡,
- 以及服务自动注册和发现。
官网Dobbo
dubbo设计架构
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
dubbo环境搭建(spring)
首先保证虚拟机已经安装过zookeeper,并且已经部署dubbo管理控制台
公共接口模块(gmall-interface)
bean
public class UserAddress implements Serializable {
private Integer id;
private String userAddress; //用户地址
private String userId; //用户id
private String consignee; //收货人
private String phoneNum; //电话号码
private String isDefault; //是否为默认地址 Y-是 N-否
public UserAddress() {
super();
}
public UserAddress(Integer id, String userAddress, String userId,
String consignee, String phoneNum,String isDefault) {
super();
this.id = id;
this.userAddress = userAddress;
this.userId = userId;
this.consignee = consignee;
this.phoneNum = phoneNum;
this.isDefault = isDefault;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getConsignee() {
return consignee;
}
public void setConsignee(String consignee) {
this.consignee = consignee;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}
}
service
public interface OrderService { //初始化订单 public void initOrder(String userId); }
public interface UserService { /** * 按照用户id返回所有的收货地址 */ public List<UserAddress> getUserAddressList(String userId); }
服务提供者(user-service)
pom.xml
导入dubbo依赖(版本2.6.2)和操作zookeeper的客户端curator的依赖(版本2.12.0)
<!--引入上面自己创建的公共接口--> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--引入dubbo依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <!--注册中心采用的是zookeeper,引入操作zookeeper的客户端--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency>
spring格式配置文件
<?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"> <!--1、指定当前应用的名字(同样的服务名字相同,不要和别的服务同名)--> <dubbo:application name="user-service-provider"></dubbo:application> <!--2、指定注册中心的位置(2种方法)--> <!--<dubbo:registry address=" zookeeper://192.168.80.130:2181?backup=192.168.80.131:2181,192.168.80.132:2181 "></dubbo:registry>--> <dubbo:registry protocol="zookeeper" address="192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181" ></dubbo:registry> <!--3、指定通信规则(通信协议,通信端口)--> <dubbo:protocol name="dubbo" port="20080"></dubbo:protocol> <!--4、暴露服务(ref:指向服务的真正实现)--> <dubbo:service interface="com.atguigu.gmall.service.UserService" ref="userService"></dubbo:service> <!--服务的实现--> <bean class="com.atguigu.gmall.service.impl.UserServiceImpl" id="userService"></bean> </beans>
接口实现类UserServiceImpl
模拟service接口实现类,配置服务提供者;
@Service public class UserServiceImpl implements UserService { @Override public List<UserAddress> getUserAddressList(String userId) { System.out.println("UserServiceImpl.....old..."); // TODO Auto-generated method stub UserAddress address1 = new UserAddress( 1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y"); UserAddress address2 = new UserAddress( 2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N"); return Arrays.asList(address1,address2); } }
主程序
public class MainApplication { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ioc= new ClassPathXmlApplicationContext("provider.xml"); ioc.start(); System.in.read();//阻塞一下,使得有时间观测 } }
服务消费者(order-service)
让服务消费者(order-service)去注册中心订阅服务提供者的服务地址
pom.xml
同服务提供者
<!--引入上面自己创建的公共接口--> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--引入dubbo依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <!--注册中心采用的是zookeeper,引入操作zookeeper的客户端--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency>
spring格式配置文件
<?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" 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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--包扫描--> <context:component-scan base-package="com.atguigu.gmall.service.impl"> </context:component-scan> <dubbo:application name="order-service-consumer"></dubbo:application> <dubbo:registry protocol="zookeeper" address=" 192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181"></dubbo:registry> <!--声明需要调用的远程服务的接口,生成远程服务代理--> <dubbo:reference interface="com.atguigu.gmall.service.UserService" id="userService"> </dubbo:reference> </beans>
接口实现类OrderServiceImpl
服务消费者调用服务提供者提供的服务来进行运行;
- 即:OrderService接口调用userService接口的方法;
@Service public class OrderServiceImpl implements OrderService { @Autowired UserService userService; @Override public void initOrder(String userId) { System.out.println("id是:"+userId); //1、查询用户收货地址 List<UserAddress> list = userService.getUserAddressList(userId); list.forEach(System.out::println); } }
主程序
public class MainApplication { @SuppressWarnings("resource") public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ioc= new ClassPathXmlApplicationContext("consumer.xml"); OrderService orderService = ioc.getBean(OrderService.class); orderService.initOrder("1"); System.out.println("调用结束"); System.in.read();//阻塞一下 } }
结果
将服务提供者和服务消费者的主程序均进行启动,之后在dubbo管理控制台就可发现如下:
dubbo环境搭建(springboot)
公共接口模块(gmall-interface)
这里保存了bean对象,service接口;service
public interface OrderService { //初始化订单 public List<UserAddress> initOrder(String userId); }
其余同上公共接口模块
服务提供者(boot-user-service-provider)
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--导入dubbo-starter--> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> <!--引入自己创建的公共接口--> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
application.yaml
dubbo: application: name: boot-user-service-provider registry: protocol: zookeeper address: 192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181 protocol: name: dubbo port: 20080
service.impl
这里面的@Service
注解来自于import com.alibaba.dubbo.config.annotation.Service;
@Service//暴露服务 //这个@Service注解并非spring里面的, //而是import com.alibaba.dubbo.config.annotation.Service; @Component public class UserServiceImpl implements UserService { @Override public List<UserAddress> getUserAddressList(String userId) { System.out.println("UserServiceImpl.....old..."); // TODO Auto-generated method stub UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y"); UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N"); return Arrays.asList(address1,address2); } }
主程序类
@EnableDubbo//开启基于注解的dubbo功能 @SpringBootApplication public class BootUserServiceProviderApplication { public static void main(String[] args) { SpringApplication.run( BootUserServiceProviderApplication.class, args); } }
服务消费者(boot-order-service-consumer)
基于web的服务消费者模块;pom.xml
<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> <!--导入dubbo-starter--> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> <!--引入自己创建的公共接口--> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
application.yaml
dubbo: application: name: boot-order-service-consumer registry: protocol: zookeeper address: 192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181
service.impl
@Service public class OrderServiceImpl implements OrderService { // @Autowired @Reference//dubbo提供的注解,帮我们远程引入这个服务,它自己会从注册中心来发现 UserService userService; @Override public List<UserAddress> initOrder(String userId) { System.out.println("id是:"+userId); //1、查询用户收货地址 List<UserAddress> list = userService.getUserAddressList(userId); return list; } }
控制层
@Controller public class OrderController { @Autowired OrderService orderService; @ResponseBody @RequestMapping("/initOrder") public List<UserAddress> initOrder(@RequestParam("uid")String userId){ return orderService.initOrder(userId); } }
主程序类
```java @EnableDubbo//开启基于注解的dubbo功能 @SpringBootApplication public class BootOrderServiceConsumerApplication { public static void main(String[] args) {
} }SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
<a name="J3R6J"></a>
## spring与springboot搭建dubbo环境总结
<a name="qa4Md"></a>
### 服务提供者
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649169533217-c72127ac-6968-4272-8852-d91997d870b0.png#clientId=u4405144a-4c0b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=362&id=uc900d0ba&margin=%5Bobject%20Object%5D&name=image.png&originHeight=407&originWidth=996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50726&status=done&style=none&taskId=u1e02eea8-002e-41a7-8425-ee469862bcd&title=&width=885.3333333333334)
<a name="ooOb9"></a>
### 服务消费者
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649169981175-b078abe2-c09d-4da9-b1a7-5328de48a9d6.png#clientId=u4405144a-4c0b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=287&id=u1b38721b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=323&originWidth=1101&originalType=binary&ratio=1&rotation=0&showTitle=false&size=36750&status=done&style=none&taskId=ub9eb0cd3-5550-469d-9a21-faba487f984&title=&width=978.6666666666666)
<a name="uAYQy"></a>
# dubbo配置
[--->>官方介绍](https://dubbo.apache.org/zh/docs/advanced/preflight-check/)
<a name="gFIgL"></a>
## 配置原则
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649211089165-8a2406ab-cf5f-4ede-87d0-09c0a80f18cd.png#clientId=u763d24a2-02f4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=309&id=u28d7d429&margin=%5Bobject%20Object%5D&name=image.png&originHeight=348&originWidth=371&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66440&status=done&style=none&taskId=ubfe2c3df-6948-48ca-a164-387893da030&title=&width=329.77777777777777)
- **JVM**启动`-D`参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
- **XML**次之,如果在XML中有配置,则`dubbo.properties`中的相应配置项无效。
- **Properties**最后,相当于缺省值,只有XML没有配置时,`dubbo.properties`的相应配置项才会生效,通常用于共享公共配置,比如应用名。
**注意:**application全局配置文件与xml文件优先级同一级别;
<a name="u0xmk"></a>
## 启动时检查
开发中有一个服务提供者,一个服务消费者,服务提供者负责将自己的服务注册到注册中心(zookeeper),服务消费者要从注册中心获取到服务提供者的调用地址,如果注册中心没有服务提供者的话,服务消费者启动的时候就会报错,因为服务消费者进行了启动时检查(`check="true"`),可以关闭启动时检查`check="false"`,这样服务消费者启动时就可以跳过检查,直接启动,只有在调用服务的时候服务消费者才到注册中心检查服务是否可用。
<a name="fJimF"></a>
### spring
```xml
<!--配置某个服务启动时不检查-->
<dubbo:reference check="false" ...
<!--或者可以通过下面的方法,统一配置消费者的统一规则,所有的服务都不检查-->
<dubbo:consumer check="false"/>
springboot
dubbo:
consumer:
check: false
超时
消费者调用提供者的方法,方法执行后返回结果给消费者,如果消费者长时间不能收到结果,就会立即终止方法,防止阻塞线程,服务消费者可以设置超时时间(默认1000ms)来实现这一功能;
spring
同样
<!--配置单个服务超时时间-->
<dubbo:reference timeout="5000" ...
<!--配置所有服务超时时间-->
<dubbo:consumer timeout="5000"/>
springboot
dubbo:
consumer:
timeout: 6000
配置覆盖关系
—->>官方文档
以 timeout 为例,其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。
总结:
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
- 如果级别不一样,则按照方法级优先,接口级次之,全局配置再次之。
重试次数
一般与超时时间配合使用,一般用于当出现网络波动而出现超时情况时造成的超时
注意:<dubbo:reference timeout="5000" retries="66" interface=... <!--retries:重试次数必须是整数,但不包括第一次-->
- 对于多个服务提供方提供同一服务的情况,如果服务消费方获取这个服务超时,并且重试次数不为1时,服务消费方会尝试从不同服务提供方获取这一服务(不会一棵树上吊死),直到重试次数用尽。
对于幂等操作(方法不管运行多少次产生的效果都不变,比如数据查询、删除、修改)设置重试次数,对于非幂等操作(方法每次运行都会产生新的效果,比如数据新增)不设置重试次数;
多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。可以按照以下的步骤进行版本迁移:
在低压力时间段,先升级一半提供者为新版本;
- 再将所有消费者升级为新版本;
-
服务提供方配置
version:提供多个版本的远程服务
<!--4、暴露服务1(ref:指向服务的真正实现)--> <dubbo:service interface="com.atguigu.gmall.service.UserService" ref="userService1" version="1.0.0"></dubbo:service> <!--服务的实现1--> <bean class="com.atguigu.gmall.service.impl.UserServiceImpl" id="userService1"></bean> <!--暴露服务2--> <dubbo:service interface="com.atguigu.gmall.service.UserService" ref="userService2" version="2.0.0"></dubbo:service> <!--服务的实现2--> <bean class="com.atguigu.gmall.service.impl.UserServiceImpl2" id="userService2"></bean>
服务消费方配置
<!--version:声明需要调用的特定版本远程服务的接口,生成远程服务代理--> <dubbo:reference timeout="6000" retries="3" version="2.0.0" interface= "com.atguigu.gmall.service.UserService" id="userService"></dubbo:reference>
本地存根
服务消费者远程调用服务提供者的功能,功能的实现在服务提供者中,服务消费者中只有远程调用的接口,有时候远程调用之前需要做一些比如参数验证,要做缓存,此时就要编写本地存根;
服务消费方
本地存根代码
/*本地存根代码*/ public class UserServiceStub implements UserService { private final UserService userService; /** * 有参构造对象,传入的是userService远程的代理对象 */ public UserServiceStub(UserService userService) { this.userService = userService; } @Override public List<UserAddress> getUserAddressList(String userId) { System.out.println("调用了本地存根:UserServiceStub"); if(!StringUtils.isEmpty(userId)){ //如果传入的userId不是空串时再进行远程调用 return userService.getUserAddressList(userId); } return null; } }
配置文件注册本地存根
<!--声明需要调用的远程服务的接口,生成远程服务代理--> <dubbo:reference interface="com.atguigu.gmall.service.UserService" id="userService" timeout="6000" retries="3" version="2.0.0" stub="com.atguigu.gmall.service.impl.UserServiceStub"/>
springboot整合dubbo的3种方式
导入
dubbo-starter
,在application
配置文件中配置属性:- 使用
@Service
注解暴露服务; - 使用
@Reference
引用服务; - 主程序类配置
@EnableDubbo
开启dubbo注解功能或者在application
配置文件中配置如下:dubbo: scan: base-packages: com.atguigu
- 使用
保留
dubbo.xml
配置文件,application
配置文件中就不需要配置dubbo
相关内容了,只需在主程序类中配置@ImportResource(locations = "classpath:dubbo.xml")
即可;- 使用注解API的方式,导入
dubbo-starter
,不需要dubbo.xml
配置文件,application
配置文件中就不需要配置dubbo
相关内容,将每一个组件手动创建到容器中(配置类),并在主配置类上面配置@EnableDubbo(scanBasePackages = "com.atguigu.gmall")
注解,并在方法实现类上面标注@Service
(import com.alibaba.dubbo.config.annotation.Service;)注解即可;- 配置类如下:
@Configuration public class MyDubboConfig { @Bean //代替”<dubbo:application...“ public ApplicationConfig applicationConfig(){ ApplicationConfig applicationConfig=new ApplicationConfig(); applicationConfig.setName("boot-provider-666"); return applicationConfig; } @Bean //代替<dubbo:registry... public RegistryConfig registryConfig(){ RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress( "192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181"); return registryConfig; } @Bean //代替<dubbo:protocol... public ProtocolConfig protocolConfig(){ ProtocolConfig protocolConfig=new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20080); return protocolConfig; } @Bean //代替<dubbo:service public ServiceConfig<UserService> serviceConfig(UserService userService){ ServiceConfig<UserService> serviceConfig=new ServiceConfig<>(); serviceConfig.setInterface(UserService.class); serviceConfig.setRef(userService); //由于接口实现类已经使用"@Component"注解注册到ioc容器中了 //所以这里方法形参里可以直接得到 serviceConfig.setTimeout(2000); serviceConfig.setVersion("1.0.0"); //配置每一个method的信息 MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("getUserAddressList");//具体方法名 methodConfig.setTimeout(1000); //将单个method的设置保存在serviceConfig里面 List<MethodConfig> methodConfigs=new ArrayList<>(); methodConfigs.add(methodConfig); serviceConfig.setMethods(methodConfigs); return serviceConfig; } //其他的配置以此类推 }
高可用
zookeeper宕机与dubbo直连
zookeeper宕机
现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务。
原因:
- 配置类如下:
- 健壮性
- 监控中心宕掉不影响使用,只是丢失部分采样数据;
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务;
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台;
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯;
- 服务提供者无状态,任意一台宕掉后,不影响使用;
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复;
dubbo直连
服务消费者可以绕过注册中心,直接与服务提供者连接;
通过@Reference(url="192.168.80.1:20080")
直接与服务提供者连接;集群下dubbo负载均衡配置
—->>>官方文档《负载均衡策略》
Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。dubbo的负载均衡机制
默认是**random**
随机的负载均衡机制:
更改dubbo的负载均衡机制
public class RandomLoadBalance extends AbstractLoadBalance { public static final String NAME = "random"; ... public class LeastActiveLoadBalance extends AbstractLoadBalance { public static final String NAME = "leastactive"; ... public class RoundRobinLoadBalance extends AbstractLoadBalance { public static final String NAME = "roundrobin"; ...
服务端服务级别
<dubbo:service interface="..." loadbalance="roundrobin" />
客户端服务级别
<dubbo:reference interface="..." loadbalance="roundrobin" />
服务端方法级别
<dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service>
客户端方法级别
<dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference>
动态调整权重
可以通过dubbo控制台直接更改:
整合hystrix-服务熔断与降级处理
服务降级
介绍
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作【对于服务消费者】。服务器降级的两种方式
(可以在dubbo控制台直接进行调整;)
mock=force:return+null
表示消费方对该服务的方法调用都直接返回null
值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。还可以改为
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回null
值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。-
集群容错
在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试;集群容错模式
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" />
整合hystrix
Hystrix旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能
【spring-cloud-starter-[hystrix](https://so.csdn.net/so/search?q=hystrix&spm=1001.2101.3001.7020)
已经弃用】原理
RPC原理
一次完整的RPC调用流程(同步调用,异步另说)如下:
-
服务消费方(client)调用以本地调用方式调用服务;
client stub
接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;client stub
找到服务地址,并将消息发送到服务端;server stub
收到消息后进行解码;server stub
根据解码结果调用本地的服务;- 本地服务执行并将结果返回给
server stub
; server stub
将返回结果打包成消息并发送至消费方;client stub
接收到消息,并进行解码;- 服务消费方得到最终结果。
netty通信原理
Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。
BIO:(Blocking IO)
NIO:(Non-Blocking IO)
Selector一般称为选择器,也可以翻译为多路复用器,用来判断应用执行状态,根据状态再来分配线程来执行下一步,四种状态如下:
- Connect(连接就绪);
- Accept(接受就绪);
- Read(读就绪);
- Write(写就绪);
Netty基本原理
基于NIO的多路复用模型来做的;
boss线程组:用来监视主线程,监听来自于dubbo.protocol.port=20882
的所有连接准备就绪事件;
worker线程组:当准备就绪之后要做什么工作,worker来做这些工作;dubbo原理
1、框架设计
—->>>官方文档2、标签解析
spring来解析配置文件中的标签都会有一个总接口BeanDefinitionParser
public void init() {//各个标签的名称在源码里写死了 this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); //这里解析"application"标签并将标签里的内容封装到ApplicationConfig里 this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); //最终调用上面DubboNamespaceHandler(dubbo的名称空间)方法 ... if (ProtocolConfig.class.equals(beanClass))... //下面进行各种标签校验
3、服务暴露
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>,BeanNameAware {...
4、服务引用
reference
标签对应ReferenceBean
解析类;
5、服务调用