服务雪崩效应
1.什么是服务雪崩效应
默认情况下,tomcat只有一个线程池去处理客户端的所有请求,这时候如果在高并发情况下,所有的请求全部访问同一个接口,导致tomcat线程池的所有线程都去处理这个一个接口服务,如果线程池的所有线程都被占用了,这时候会导致其他接口请求没有可用的线程来请求,新来的接口请求直接无响应,这就是服务雪崩效应效应。
服务雪崩可能会造成连环雪崩效应,最终导致整个微服务系统不可用
tomcat8线程池中最大活跃线程数默认是200 ?
2.服务雪崩演示
上图中,订单服务order 的/orderToMemberUserInfo接口通过Feign客户端调用 member服务 /getUserInfo接口,在/getUserInfo接口中,设置睡眠1.5秒之后再返回结果,修改order服务的线程池最大活跃线程数量,通过jmeter发起测试
2.1 演示代码
- MemberServiceImpl
package com.lchtest.api.service.impl;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.service.IMemberService;
import com.lchtest.common.base.BaseApiService;
import com.lchtest.common.base.ResponseBase;
/**
* memberService的业务实现
* @author pc
*
*/
@RestController
public class MemberServiceImpl extends BaseApiService implements IMemberService {
@Value("${server.port}")
private String serverPort;
@GetMapping("/")
public String index(HttpServletRequest req) {
System.out.println("我是首页.....");
return "我是member服务" + serverPort;
}
@RequestMapping("/getMember")
@Override
public UserEntity getMember(String name) {
UserEntity userEntity = new UserEntity();
userEntity.setName(name);
return userEntity;
}
// 测试服务雪崩效应,这里模拟接口处理延迟
@RequestMapping("/getUserInfo")
@Override
public ResponseBase getUserInfo() {
try {
System.out.println("进入memberservice-getUserInfo(),开始处理/getUserInfo接口请求");
// 会员服务接口产生1.5s延迟
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return setResultSuccess("订单服务接口调用会员服务接口成功.");
}
}
- Order服务相关代码:
yaml配置文件 :设置最大活跃线程数为10;配置Feign客户端超时时间
#服务启动端口号
server:
port: 8005
#设置最多同时10个线程去处理请求
tomcat:
max-threads: 10
#服务名称(服务注册到eureka名称)
spring:
application:
name: app-order
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:8100/eureka
#把自己注册到注册中心
register-with-eureka: true
# 从eureka上获取注册信息
fetch-registry: true
ribbon:
# 不设置超时时间,调用会员服务1.5s延时的接口时会报 redaTimeOut 错误
# 建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
# 建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectionTimeout: 5000
Feign客户端 MemberServiceFeign
package com.lchtest.api.feign;
import org.springframework.cloud.openfeign.FeignClient;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.service.IMemberService;
/**
* Feign客户端定义,继承IMemberService,这样就可以避免类似下面这样的重复代码了: @RequestMapping("/getMember")
* public UserEntity getMember(String name);
*
* @author pc
*
*/
@FeignClient(name = "app-member")
public interface MemberServiceFeign extends IMemberService {
}
member服务接口项目中的接口定义:
public interface IMemberService {
// 实体类放在接口项目里面比较好,可能其他项目也会依赖该实体类,代码实现放到接口的实现里面
@RequestMapping("/getMember")
public UserEntity getMember(@RequestParam("name") String name);
@RequestMapping("/getUserInfo")
public ResponseBase getUserInfo();
}
Order服务接口定义:
package com.lchtest.api.service;
import org.springframework.web.bind.annotation.RequestMapping;
import com.lchtest.common.base.ResponseBase;
public interface IOrderService {
// 订单服务调用会员服务接口
@RequestMapping("/orderToMember")
public String orderToMember(String name);
// 订单服务调用会员服务接口
@RequestMapping("/orderToMemberUserInfo")
public ResponseBase orderToUserInfo();
@RequestMapping("/orderInfo")
public ResponseBase orderInfo();
}
ordder服务接口实现:OrderServiceImpl
package com.lchtest.api.service.impl;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.feign.MemberServiceFeign;
import com.lchtest.api.service.IOrderService;
import com.lchtest.common.base.BaseApiService;
import com.lchtest.common.base.ResponseBase;
/**
* 订单服务继承会员服务接口,用来实现Feign客户端,减少重复接口代码!
*
* @author pc
*
*/
@RestController
public class OrderServiceImpl extends BaseApiService implements IOrderService {
@Autowired
private MemberServiceFeign memberServiceFeign;
@Value("${server.port}")
private String serverPort;
@GetMapping("/")
public String index(HttpServletRequest req) {
System.out.println("我是首页.....");
return "我是order服务" + serverPort;
}
/**
* http://localhost:8005/orderToMember?name=admin
*/
@RequestMapping("/orderToMember")
@Override
public String orderToMember(String name) {
UserEntity user = memberServiceFeign.getMember(name);
return user == null ? "no user find" : user.toString();
}
/**
* 测试服务雪崩效应 -没有解决雪崩效应 对该接口在member服务中的实现设置延迟时间,假设该接口需要1.5s才能处理完
* 没有com.lchtest.api.fallback.MemberServiceFallback这个类的情况下
* @return
*/
@RequestMapping("/orderToMemberUserInfo")
public ResponseBase orderToUserInfo() {
System.out.println("orderToMemberUserInfo: 当前线程池名称" + Thread.currentThread().getName());
return memberServiceFeign.getUserInfo();
}
@RequestMapping("/getOrderInfo")
public String getOrderInfo() {
System.out.println("getOrderInfo: 当前线程池名称" + Thread.currentThread().getName());
return "getOrderInfo success.";
}
// 订单服务接口
@Override
public ResponseBase orderInfo() {
return setResultSuccess();
}
}
启动类:
package com.lchtest.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //开启Feign客户端
//@EnableHystrix //开启服务熔断
public class AppOrder {
public static void main(String[] args) {
SpringApplication.run(AppOrder.class, args);
}
}
代码准备完毕,启动uereka注册中心,member服务,order服务,测试order接口正常:
2.2 jmeter设置以及服务雪崩效应演示
- 启动jmeter,设置线程数,压测的接口等参数
双击启动jmeter,右键单击测试计划,点击添加->Threads-> 线程组,设置并发量:
order服务设置tomcat能够同时处理请求的数量为10
右键单击之前创建的线程组,点击添加-> Sampler -> Http请求,创建http请求:
点击绿色三角形按钮,开始压测:
然后浏览器打开调试模式,去访问没有延时的接口http://localhost:8005/getOrderInfo,可以看到这个接口迟迟不能响应,浏览器一直转圈,并提示正在等待localhost的响应,这种现象就是服务雪崩效应。
3. Feign客户端超时时间配置
SpringCloud Feign客户端默认开启ribbon(可以启动两个member服务,通过order服务调用member服务查看是否有负载均衡),并且默认超时时间为1s ,当Feign客户端调用一个接口时,如果接口在1s内没有能够及时返回响应,那么Feign客户端会报read time out错误:
可以设置Feign客户端超时时间来解决上面的报错,如下:
###设置feign客户端超时时间
ribbon:
###指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
ReadTimeout: 5000
###指的是建立连接后从服务器读取到可用资源所用的时间。
ConnectTimeout: 5000
代码地址
使用到的项目:
springcloud2.0-eureak-server
springcloud2.0-feign-parent