服务降级
解决高并发的三把利器:降级、限流、缓存。
1 什么是服务降级
服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务有策略的降低服务级别,以释放服务器资源,保证核心任务的正常运行。
2 服务降级方式
能够实现服务降级方式很多,常见的有如下几种情况:
- 部分服务暂停
系统可以访问,一部分业务权重相对低的服务暂时停止服务,例,某猫光棍节平台不通过修改收货地址、不可查看历史订单等; - 全部服务暂停
系统不可访问,请求会被告知系统维护中,跳转至预设静态资源,例,12306在晚23点至次日早上7点不提供服务; - 随机拒绝服务
系统可以访问,所有服务都提供,随机拒绝用户请求,按批次接收请求,在每批次中按照比例随机拒绝部分用户,例,部分平台访问过程中出现系统忙请重试; - 部分服务延迟
系统可以访问,部分高峰功能采用延迟应答,即排队返回结果;
3 整个系统的服务降级埋点
网关路由:直接将请求路由到静态页面,即停止请求继续向下流转,返回静态资源给请求用户,全部服务暂停方式常用;
消费者:dubbo与springcloud的服务降级一般就指此处,消费者端接收请求后给出响应,停止服务继续流转,响应结果并不是提供者运算出来,而是预先设定的结果集,根据请求不同返回固定结果,减去后方处理压力;
数据缓存层:预先设定某种请求,如查询类请求,无论提交参数如何,只要是预设查询就返回既定结果,此结果就存放在数据缓存中,不需要在向下请求;
消息中间件:经常处理部分服务延迟方式,接收到请求,然后将请求放入MQ中进行排队等候处理,提供者处理完成后在给予响应;注意,如同部分服务延迟方式与其他三种方式不同一样,消息中间件的降级与其他埋点也有同样的不同,即最终返回结果一定是正确处理请求结果,而其他的降级处理返回的都是假设结果;
提供者:同样预设特定返回,不进入数据层查询,减去数据层压力,普遍约定不在提供者处做服务降级,因为此处再做意义不大,效率提升有限。
4 dubbo的服务降级采用Mock机制
Dubbo的服务降级采用的是mock机制,其具有两种降级处理方式:Mock Null降级处理,与Mock Class降级处理(推荐使用)。
5 Mock Null服务降级处理
降级处理在消费者埋点进行,因此无需创建提供者,同时也模拟提供者宕机情况下的降级处理逻辑;
(1) 创建消费者工程并引入依赖
创建消费者工程consumer-mocknull;
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 自定义版本号 -->
<spring-version>4.3.16.RELEASE</spring-version>
</properties>
<dependencies>
<!-- dubbo依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- zk客户端依赖:curator -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.13.0</version>
</dependency> <dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.13.0</version>
</dependency>
<!-- commons-logging依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
(2) 定义接口
public interface UserService {
String getUsernameById(int id);
void addUser(String username);
}
(3) 创建spring-consumer.xml配置文件
<!-- 添加 DUBBO SCHEMA -->
<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">
<!-- 指定当前工程在Monitor监控中心显示的名称,一般与工程名相同 -->
<dubbo:application name="consumer-morknull"/>
<!-- 声明zookeeper注册中,单机zk -->
<dubbo:registry address="zookeeper://192.168.85.129:2181" />
<!-- 服务消费者-->
<dubbo:reference id="userService" mock="return null" check="false"
interface="com.zxy.service.UserService"/>
</beans>
(5) 创建消费者启动类
/**
* Mock Null服务降级处理用户体验不好
*/
public class ConsumerRun {
public static void main(String[] args) throws IOException {
// 创建Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-consumer.xml");
//从容器中获取服务
UserService service = (UserService)applicationContext.getBean("userService");
//Mock Null服务降级处理对于有返回值的方法,其返回结果是null
String usernameById = service.getUsernameById(5);
System.out.println(usernameById);
//Mock Null服务降级处理对于无返回值的方法,其没有任何返回结果
service.addUser("dubbo");
}
}
7 Mock Class服务降级处理
(1) 创建消费者工程导入依赖
创建工程consumer-mockclass,依赖与consumer-mocknull相同;
(2) 定义Mock Class
在业务接口所在的包中,本例为com.zxy.service包,定义一个类,该类的命名需要满足以下规则:业务接口简单类名 + Mock;
/**
* 预设降级后返回结果
*/
public class UserServiceMock implements UserService{
@Override
public String getUsernameById(int id) {
return "没有此用户:"+id;
}
@Override
public void addUser(String username) {
System.out.println("添加用户失败:"+username);
}
}
(3) 创建spring-consumer.xml
<!-- 添加 DUBBO SCHEMA -->
<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">
<!-- 指定当前工程在Monitor监控中心显示的名称,一般与工程名相同 -->
<dubbo:application name="9-consumer-morknull"/>
<!-- 声明zookeeper注册中,单机zk -->
<dubbo:registry address="zookeeper://192.168.85.129:2181" />
<!-- 服务消费者-->
<dubbo:reference id="userService" mock="true" check="false"
interface="com.zxy.service.UserService"/>
</beans>