基于springcloud异步线程池、高并发请求feign的解决方案

ScenTaskTestApplication.java

package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author scen
* @version 2018年9月27日 上午11:51:04
*/
@EnableFeignClients
@SpringBootApplication
public class ScenTaskTestApplication {
 public static void main(String[] args) {
  SpringApplication.run(ScenTaskTestApplication.class, args);
 }
}

application.properties

spring.application.name=scen-task-test
server.port=9009
feign.hystrix.enabled=true
#熔断器失败的个数==进入熔断器的请求达到1000时服务降级(之后的请求直接进入熔断器)
hystrix.command.default.circuitBreaker.requestVolumeThreshold=1000
#回退最大线程数
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=50
#核心线程池数量
hystrix.threadpool.default.coreSize=130
#请求处理的超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=100000
ribbon.ReadTimeout=120000
#请求连接的超时时间
ribbon.ConnectTimeout=130000
eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${server.port}}
eureka.instance.preferIpAddress=true
eureka.client.service-url.defaultZone=http://127.0.0.1:9000/eureka
logging.level.com.test.user.service=debug
logging.level.org.springframework.boot=debug
logging.level.custom=info

AsyncConfig.java

package com.test;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
 * springboot异步线程池配置
 * @author Scen
 * @date 2018/11/7 18:28
 */
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

 @Override
 public Executor getAsyncExecutor() {
  //定义线程池
  ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  //核心线程数
  taskExecutor.setCorePoolSize(20);
  //线程池最大线程数
  taskExecutor.setMaxPoolSize(100);
  //线程队列最大线程数
  taskExecutor.setQueueCapacity(10);
  //初始化
  taskExecutor.initialize();
  return taskExecutor;
 }
}

DoTaskClass.java

package com.test;
import com.test.pojo.User;
import com.test.pojo.UserEducation;
import com.test.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * 任务类 定义异步工作任务
 * @author Scen
 * @date 2018/11/7 18:40
 */
@Component
public class DoTaskClass {
 /**
  * 一个feign的客户端
  */
 private final UserService userService;

 @Autowired
 public DoTaskClass(UserService userService) {
  this.userService = userService;
 }

 /**
  * 核心任务
  *
  * @param uid
  */
 @Async
 public void dotask(String uid) {
  /**
   * 模拟复杂工作业务(109个线程同时通过feign请求微服务提供者)
   */
  {
   List<UserEducation> userEducationByUid = userService.findUserEducationByUid(uid);
   List<String> blackList = userService.getBlackList();
   String userSkilled = userService.getUserSkilled(uid);
   String userFollow = userService.getUserFollow(uid);
   User userById = userService.getUserById(uid);
   List<String> followList = userService.getFollowList(uid);
   int userActivityScore = userService.getUserActivityScore(uid);
  }
//  打印线程名称分辨是否为多线程操作
  System.out.println(Thread.currentThread().getName() + "===任务" + uid + "执行完成===");
 }
}

TestController.java

package com.test;
import com.test.pojo.User;
import com.test.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 测试案例
 * @author Scen
 * @date 2018/11/7 18:10
 */
@RestController
public class TestController {

 /**
  * 此处仅用此feign客户端请求微服务获取核心工作所需参数
  */
 private final UserService userService;

 /**
  * 核心工作异步算法
  */
 private final DoTaskClass doTaskClass;

 @Autowired
 public TestController(DoTaskClass doTaskClass, UserService userService) {
  this.doTaskClass = doTaskClass;
  this.userService = userService;
 } 

 /**
  * 手动触发工作
  * @throws InterruptedException
  */
 @RequestMapping("/test")
 public void task() throws InterruptedException {
  /*
   取到1000个要执行任务的必备参数
   */
  List<User> userList = userService.findAllLite(1, 1000);
  for (int i = 0; i < userList.size(); i++) {
   try {
//    异步线程开始工作
    doTaskClass.dotask(userList.get(i).getId());
   } catch (Exception e) {
    /*
     若并发线程数达到MaxPoolSize+QueueCapacity=110(参考AsyncConfig配置)会进入catch代码块
     i--休眠3秒后重试(尝试进入线程队列:当且仅当109个线程有一个或多个线程完成异步任务时重试成功)
     */
    i--;
    Thread.sleep(3000*3);
   }
   System.out.println(i);
  }
 }
}

相关线程池、超时时间等数量和大小按实际业务配置

补充:SpringCloud关于@FeignClient和Hystrix集成对http线程池监控问题

@FeignClient可以作为Http代理访问其他微服务节点,可以用apache的httpclient替换@FeignClient原生的URLConnection请求方式,以达到让http请求走Http线程池的目的。

而@FeignClient和hystrix集成之后,在hystrix dashboard上可以监控到 @FeignClient 中接口调用情况和 @FeignClient 中httpclient中线程池使用状况。

下面是demo的示例:

1、@FeignClient的接口代码如下:

@FeignClient(value="service-A", fallback=ServiceClientHystrix.class)
public interface ServiceClient {
 @RequestMapping(method = RequestMethod.GET, value = "/add/{id}")
 String add(@PathVariable("id") Integer id);
}

2、ServiceClientHystrix.java

@Component
public class ServiceClientHystrix implements ServiceClient{
 @Override
 public String add(Integer id) {
  return "add value from ServiceClientHystrix";
 }
}

3、关于@FeignClient和hystrix

集成后,Http线程池配置如下:

hystrix.threadpool.服务实例ID.参数

例如设置httpclient的线程池最大线程数量

hystrix.threadpool.service-A.coreSize=20//默认是hystrix.threadpool.default.coreSize = 10
hystrix.threadpool.service-A.maximumSize=20//默认是hystrix.threadpool.default.maximumSize = 10

启动服务后用测试用例连续调用接口测试,用hystrix dashboard

监控得到下图监控效果:

去掉hystrix.threadpool.服务实例ID.参数配置后,再次用测试用例调用接口得到监控如下图:

PoolSize的大小取决于hystrix.threadpool.服务实例ID.coreSize大小设置

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • Spring Boot使用Spring的异步线程池的实现

    前言 线程池,从名字上来看,就是一个保存线程的"池子",凡事都有其道理,那线程池的好处在哪里呢? 我们要让计算机为我们干一些活,其实都是在使用线程,使用方法就是new一个Runnable接口或者新建一个子类,继承于Thread类,这就会涉及到线程对象的创建与销毁,这两个操作无疑是耗费我们系统处理器资源的,那如何解决这个问题呢? 线程池其实就是为了解决这个问题而生的. 线程池提供了处理系统性能和大用户量请求之间的矛盾的方法,通过对多个任务重用已经存在的线程对象,降低了对线程对象创建和销毁

  • 详解SpringBoot中异步请求和异步调用(看完这一篇就够了)

    一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应.一句话:增加了服务器对客户端请求的吞吐量(实际生产上我们用的比较少,如果并发请求量很大的情况下,我们会通过nginx把请求负载到集群服务的各个节点上来分摊请求压力,当然还可以通过消息队列来做请求的缓冲). 2.异步请求的实现 方式一:Servlet方式实现异步请求

  • springboot中@Async默认线程池导致OOM问题

    前言: 1.最近项目上在测试人员压测过程中发现了OOM问题,项目使用springboot搭建项目工程,通过查看日志中包含信息:unable to create new native thread 内存溢出的三种类型: 1.第一种OutOfMemoryError: PermGen space,发生这种问题的原意是程序中使用了大量的jar或class 2.第二种OutOfMemoryError: Java heap space,发生这种问题的原因是java虚拟机创建的对象太多 3.第三种OutOfM

  • 解决Spring Cloud Feign 请求时附带请求头的问题

    问题描述 Feign 在请求时是不会将 request 的请求头带着请求的,导致假如 Feign 调用的接口需要请求头的信息,比如当前用户的 token 之类的就获取不到 解决方案 FeignConfiguration 通过实现 Feign 的 RequestInterceptor 将从上下文中获取到的请求头信息循环设置到 Feign 请求头中. /** * feign 配置文件 * 将请求头中的参数,全部作为 feign 请求头参数传递 * @author: linjinp * @create

  • 基于springcloud异步线程池、高并发请求feign的解决方案

    ScenTaskTestApplication.java package com.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @author scen *

  • php处理抢购类功能的高并发请求

    本文以抢购.秒杀为例.介绍如何在高并发状况下确保数据正确. 在高并发请求下容易参数两个问题 1.数据出错,导致产品超卖. 2.频繁操作数据库,导致性能下降. 测试环境 Windows7 apache2.4.9 php5.5.12 php框架 yii2.0 工具 apache bench (apache自带高并发请求工具). 通常处理方法 从控制器可以看出代码思路.先查询商品库存.如果库存大于0 ,则库存减少1,同时生产订单,录入抢购者数据. // 常规代码处理高并发 public functio

  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    网站面对高并发的情况下,除了增加硬件, 优化程序提高以响应速度外,还可以通过并行改串行的思路来解决.这种思想常见的实践方式就是数据库锁和消息队列的方式.这种方式的缺点是需要排队,响应速度慢,优点是节省成本. 演示一下现象 创建一个在售产品表 CREATE TABLE [dbo].[product]( [id] [int] NOT NULL,--唯一主键 [name] [nvarchar](50) NULL,--产品名称 [status] [int] NULL ,--0未售出 1 售出 默认为0

  • Spring Boot之@Async异步线程池示例详解

    目录 前言 一. Spring异步线程池的接口类 :TaskExecutor 二.简单使用说明 三.定义通用线程池 1.定义线程池 2.异步方法使用线程池 3.通过xml配置定义线程池 四.异常处理 五.问题 前言 很多业务场景需要使用异步去完成,比如:发送短信通知.要完成异步操作一般有两种: 1.消息队列MQ 2.线程池处理. 我们来看看Spring框架中如何去使用线程池来完成异步操作,以及分析背后的原理. 一. Spring异步线程池的接口类 :TaskExecutor 在Spring4中,

  • SpringBoot使用异步线程池实现生产环境批量数据推送

    目录 前言 编写线程池配置类 编写异步服务 异步批量上报数据 总结 前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在公司是医疗行业,敏感数据需要上报到某监管平台,所以有一个定时任务在流量较小时(一般是凌晨后)执行上报行为.但特殊时期会存在一定要在工作时间大批量上报数据的情况,且要求短时间内就要完成,此时就考虑写一个专门的异步上报接口手动执行,利用

  • 浅谈Spring @Async异步线程池用法总结

    本文介绍了Spring @Async异步线程池用法总结,分享给大家,希望对大家有帮助 1. TaskExecutor spring异步线程池的接口类,其实质是Java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程. 2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作.只适用于不需要多线程的地方 3. Conc

  • C#实现控制线程池最大数并发线程

    1. 实验目的: 使用线程池的时候,有时候需要考虑服务器的最大线程数目和程序最快执行所有业务逻辑的取舍. 并非逻辑线程越多也好,而且新的逻辑线程必须会在线程池的等待队列中等待 ,直到线程池中工作的线程执行完毕, 才会有系统线程取出等待队列中的逻辑线程,进行CPU运算. 2.  解决问题: <a>如果不考虑服务器实际可支持的最大并行线程个数,程序不停往线程池申请新的逻辑线程,这个时候我们可以发现CPU的使用率会不断飙升,并且内存.网络带宽占用也会随着逻辑线程在CPU队列中堆积,而不断增大. &l

  • Android线程池控制并发数多线程下载

    多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销. 这些开销反而会导致下载速度降低.因此需要避免在代码中直接开启大量线程执行下载. 主要实现步奏: 1.定义一个DownUtil类,下载工作基本在此类完成,在构造器中初始化UI线程的Handler.用于子线程和UI线程传递下载进度值. 2.所有的下载任务都保存在LinkedList.在init()方法中开启一个后台线程,不断地从LinkedList中取任务交给线程池中的空闲线程执

  • @Async异步线程池以及线程的命名方式

    本文记录@Async的基本使用以及通过实现ThreadFactory来实现对线程的命名. @Async的基本使用 近日有一个道友提出到一个问题,大意如下: 业务场景需要进行批量更新,已有数据id主键.更新的状态.单条更新性能太慢,所以使用in进行批量更新.但是会导致锁表使得其他业务无法访问该表,in的量级太低又导致性能太慢. 龙道友提出了一个解决方案,把要处理的数据分成几个list之后使用多线程进行数据更新.提到多线程可直接使用@Async注解来进行异步操作. 好的,接下来上面的问题我们不予解答

随机推荐