<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          利用 Nacos 實現(xiàn)了一個動態(tài)化線程池,非常實用!

          共 12457字,需瀏覽 25分鐘

           ·

          2023-01-12 15:45

          點擊關(guān)注公眾號:互聯(lián)網(wǎng)架構(gòu)師,后臺回復(fù) 2T獲取2TB學(xué)習(xí)資源!

          上一篇:Alibaba開源內(nèi)網(wǎng)高并發(fā)編程手冊.pdf

          在后臺開發(fā)中,會經(jīng)常用到線程池技術(shù),對于線程池核心參數(shù)的配置很大程度上依靠經(jīng)驗。然而,由于系統(tǒng)運行過程中存在的不確定性,我們很難一勞永逸地規(guī)劃一個合理的線程池參數(shù)。在對線程池配置參數(shù)進行調(diào)整時,一般需要對服務(wù)進行重啟,這樣修改的成本就會偏高。一種解決辦法就是,將線程池的配置放到平臺側(cè),運行開發(fā)同學(xué)根據(jù)系統(tǒng)運行情況對核心參數(shù)進行動態(tài)配置。

          本文以Nacos作為服務(wù)配置中心,以修改線程池核心線程數(shù)、最大線程數(shù)為例,實現(xiàn)一個簡單的動態(tài)化線程池。

          代碼實現(xiàn)

          1.依賴

          <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              <version>2021.1</version>
          </dependency>
          <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
              <version>2021.1</version>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter</artifactId>
          </dependency>

          2.配置yml文件

          bootstrap.yml:

          server:
            port: 8010
            # 應(yīng)用名稱(nacos會將該名稱當做服務(wù)名稱)
          spring:
            application:
              name: order-service
            cloud:
              nacos:
                discovery:
                  namespace: public
                  server-addr: 192.168.174.129:8848
                config:
                  server-addr: 192.168.174.129:8848
                  file-extension: yml

          application.yml:

          spring:
            profiles:
              active: dev

          為什么要配置兩個yml文件?

          springboot中配置文件的加載是存在優(yōu)先級順序的,bootstrap優(yōu)先級高于application。

          nacos在項目初始化時,要保證先從配置中心進行配置拉取,拉取配置之后才能保證項目的正常啟動。

          3.nacos配置

          登錄到nacos管理頁面,新建配置,如下圖所示:

          注意Data ID的命名格式為,${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension} ,在本文中,Data ID的名字就是order-service-dev.yml。

          這里我們只配置了兩個參數(shù),核心線程數(shù)量和最大線程數(shù)。

          4.線程池配置和nacos配置變更監(jiān)聽

          @RefreshScope
          @Configuration
          public class DynamicThreadPool implements InitializingBean {
              @Value("${core.size}")
              private String coreSize;
           
              @Value("${max.size}")
              private String maxSize;
           
              private static ThreadPoolExecutor threadPoolExecutor;
           
              @Autowired
              private NacosConfigManager nacosConfigManager;
           
              @Autowired
              private NacosConfigProperties nacosConfigProperties;
           
              @Override
              public void afterPropertiesSet() throws Exception {
                  //按照nacos配置初始化線程池
                  threadPoolExecutor = new ThreadPoolExecutor(Integer.parseInt(coreSize), Integer.parseInt(maxSize), 10L, TimeUnit.SECONDS,
                          new LinkedBlockingQueue<>(10),
                          new ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
                          new RejectedExecutionHandler() {
                              @Override
                              public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                  System.out.println("rejected!");
                              }
                          });
           
                  //nacos配置變更監(jiān)聽
                  nacosConfigManager.getConfigService().addListener("order-service-dev.yml", nacosConfigProperties.getGroup(),
                          new Listener() {
                              @Override
                              public Executor getExecutor() {
                                  return null;
                              }
           
                              @Override
                              public void receiveConfigInfo(String configInfo) {
                                  //配置變更,修改線程池配置
                                  System.out.println(configInfo);
                                  changeThreadPoolConfig(Integer.parseInt(coreSize), Integer.parseInt(maxSize));
                              }
                          });
              }
           
              /**
               * 打印當前線程池的狀態(tài)
               */

              public String printThreadPoolStatus() {
                  return String.format("core_size:%s,thread_current_size:%s;" +
                                  "thread_max_size:%s;queue_current_size:%s,total_task_count:%s", threadPoolExecutor.getCorePoolSize(),
                          threadPoolExecutor.getActiveCount(), threadPoolExecutor.getMaximumPoolSize(), threadPoolExecutor.getQueue().size(),
                          threadPoolExecutor.getTaskCount());
              }
           
              /**
               * 給線程池增加任務(wù)
               *
               * @param count
               */

              public void dynamicThreadPoolAddTask(int count) {
                  for (int i = 0; i < count; i++) {
                      int finalI = i;
                      threadPoolExecutor.execute(new Runnable() {
                          @Override
                          public void run() {
                              try {
                                  System.out.println(finalI);
                                  Thread.sleep(10000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                      });
                  }
              }
           
              /**
               * 修改線程池核心參數(shù)
               *
               * @param coreSize
               * @param maxSize
               */

              private void changeThreadPoolConfig(int coreSize, int maxSize) {
                  threadPoolExecutor.setCorePoolSize(coreSize);
                  threadPoolExecutor.setMaximumPoolSize(maxSize);
              }
          }

          這個代碼就是實現(xiàn)動態(tài)線程池和核心了,需要說明的是:

          • @RefreshScope:這個注解用來支持nacos的動態(tài)刷新功能;

          • @Value("${max.size}"),@Value("${core.size}"):這兩個注解用來讀取我們上一步在nacos配置的具體信息;同時,nacos配置變更時,能夠?qū)崟r讀取到變更后的內(nèi)容

          • nacosConfigManager.getConfigService().addListener:配置監(jiān)聽,nacos配置變更時實時修改線程池的配置。

          5.controller

          為了觀察線程池動態(tài)變更的效果,增加Controller類。

          @RestController
          @RequestMapping("/threadpool")
          public class ThreadPoolController {
           
              @Autowired
              private DynamicThreadPool dynamicThreadPool;
           
              /**
               * 打印當前線程池的狀態(tài)
               */

              @GetMapping("/print")
              public String printThreadPoolStatus() {
                  return dynamicThreadPool.printThreadPoolStatus();
              }
           
              /**
               * 給線程池增加任務(wù)
               *
               * @param count
               */

              @GetMapping("/add")
              public String dynamicThreadPoolAddTask(int count) {
                  dynamicThreadPool.dynamicThreadPoolAddTask(count);
                  return String.valueOf(count);
              }
          }

          6.測試

          啟動項目,訪問http://localhost:8010/threadpool/print打印當前線程池的配置。

          可以看到,這個就是我們之前在nacos配置的線程數(shù)。

          訪問http://localhost:8010/threadpool/add?count=20增加20個任務(wù),重新打印線程池配置

          可以看到已經(jīng)有線程在排隊了。

          為了能夠看到效果,我們多訪問幾次/add接口,增加任務(wù)數(shù),在控制臺出現(xiàn)拒絕信息時調(diào)整nacos配置。

          此時,執(zhí)行/add命令時,所有的線程都會提示rejected。

          調(diào)整nacos配置,將核心線程數(shù)調(diào)整為50,最大線程數(shù)調(diào)整為100.

          重新多次訪問/add接口增加任務(wù),發(fā)現(xiàn)沒有拒絕信息了。這時,打印具體的線程狀態(tài),發(fā)現(xiàn)線程池參數(shù)修改成功。

          總結(jié)

          這里,只是簡單實現(xiàn)了一個可以調(diào)整核心線程數(shù)和最大線程數(shù)的動態(tài)線程池。具體的線程池實現(xiàn)原理可以參考美團的這篇文章:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html,結(jié)合監(jiān)控告警等實現(xiàn)一個完善的動態(tài)線程池產(chǎn)品。

          優(yōu)秀的輪子還有好多,比如Hippo4J ,使用起來和dynamic-tp差不多。Hippo4J 有無依賴中間件實現(xiàn)動靜線程池,也有默認實現(xiàn)Nacos和Apollo的版本,而dynamic-tp 默認實現(xiàn)依賴Nacos或Apollo。


          最后,關(guān)注公眾號互聯(lián)網(wǎng)架構(gòu)師,在后臺回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全。


          正文結(jié)束


          推薦閱讀 ↓↓↓

          1.Redis 只會用緩存?16種妙用讓同事直呼牛X

          2.從零開始搭建創(chuàng)業(yè)公司后臺技術(shù)棧

          3.程序員一般可以從什么平臺接私活?

          4.流程引擎的架構(gòu)設(shè)計

          5.為什么國內(nèi) 996 干不過國外的 955呢?

          6.中國的鐵路訂票系統(tǒng)在世界上屬于什么水平?                        

          7.15張圖看懂瞎忙和高效的區(qū)別!

          瀏覽 97
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日二区在线观看 | 亚洲国产成人7777 | 国产精品女人久久久 | 日韩无码一卡二卡三卡 | 成人三级电影久久 |