微服务:SpringCloud 高并发出现的问题以及解决办法(一篇文章给你安排的明明白白的!!!)
之前的做文章讲述了一些常用的组件,这次我们来聊聊基于上面组件的高并发问题。我们先以高并发时,项目程序出现的现象入手。一、修改项目order-service这里面我们不采用feign,而是采用普通的http请求的方式,用restTemplate。这里有两个接口。@RestController@RequestMapping("/order")public class OrderController {
之前的做文章讲述了一些常用的组件,这次我们来聊聊基于上面组件的高并发问题。我们先以高并发时,项目程序出现的现象入手。
一、修改项目
order-service
这里面我们不采用feign,而是采用普通的http请求的方式,用restTemplate。这里有两个接口。
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductFeginClient productFeginClient;
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/buy/{id}", method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
// Product product = productFeginClient.findById(id);
Product product = restTemplate.getForObject("http://localhost:9001/product/1",Product.class);
return product;
}
@RequestMapping(value = "/order", method = RequestMethod.GET)
public String findByIdHei() {
return "hehhe";
}
修改配置文件(设置最大线程数):
product-service
利用线程模拟一个耗时的操作。
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Product product = productService.findById(id);
product.setProductName("访问的服务地址:"+ip + ":" + port);
return product;
}
不要纠结端口了好吧,一定是能够调用的。
我们来访问一下这两个端口。6.38s,证明耗时操作是可以得。
293ms,访问很快。
二、搞一个压力测试工具
下载一个jmeter
配置线程组(每次20个线程访问,循环50个)
配置请求得地址
启动测试
启动之后访问/order/order接口。访问变成了6s多。这就是高并发出现的问题,影响了其他接口的正常访问
三、问题分析
我们看这张图,如果用户很多的话,并发量很高,我们的tomcat设置最大并发数是10。tomcat底层处理请求是一个线程池(要知道),最大线程数就是10,其余的请求多余,都会进入一个阻塞的有界队列中,之后等某个线程空闲,再从队列中取出,处理。
一个接口高并发的时候,我们发现另一个接口会变慢,主要原因就是起初所有的线程资源都被上一个接口占用。导致访问下面接口需要进入队列。如果并发量特别巨大的时候,可能会导致我们的程序崩溃或者服务器瘫痪。
如何解决?
有一种方案是,给两个接口都设立一个线程池,分别处理对应的接口请求。
四、实现用线程池隔离
引入依赖:
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>
引入新类。这个就是我们单独配置的线程隔离。设置的线程数5很重要。(因为tomcat一共有10个,给了其中最大5个,剩余的自然是下一个接口的了)
package com.springcloud.demo.command;
import com.netflix.hystrix.*;
import com.springcloud.demo.entity.Product;
import org.springframework.web.client.RestTemplate;
public class OrderCommand extends HystrixCommand<Product> {
private RestTemplate restTemplate;
private Long id;
public OrderCommand(RestTemplate restTemplate, Long id) {
super(setter());
this.restTemplate = restTemplate;
this.id = id;
}
private static Setter setter() {
// 服务分组
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product");
// 服务标识
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product");
// 线程池名称
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool");
/**
* 线程池配置
* withCoreSize : 线程池大小为10
* withKeepAliveTimeMinutes: 线程存活时间15秒
* withQueueSizeRejectionThreshold :队列等待的阈值为100,超过100执行拒绝策略
*/
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(5)
.withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
// 命令属性配置Hystrix 开启超时
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
// 采用线程池方式实现服务隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
// 禁止
.withExecutionTimeoutEnabled(false);
return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
}
@Override
protected Product run() throws Exception {
return restTemplate.getForObject("http://localhost:9001/product/"+id, Product.class);
}
/**
* 降级方法!!
*/
@Override
protected Product getFallback(){
Product product = new Product();
product.setProductName("不好意思,出错了!!!");
return product;
}
}
修改接口方法。
@RequestMapping(value = "/buy/{id}", method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
// Product product = productFeginClient.findById(id);
// Product product = restTemplate.getForObject("http://localhost:9001/product/1",Product.class);
Product product = new OrderCommand(restTemplate,id).execute();
return product;
}
重新启动就可以了。再次测试,访问接口时间
更多推荐
所有评论(0)