Nacos动态线程池完整实现方案

Nacos动态线程池完整实现方案

1. 依赖配置 (pom.xml)

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    <version>2.2.9.RELEASEversion>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
2. Nacos配置 (bootstrap.yml)
spring:
  application:
    name: big-market-app
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: 71f7c141-xxxx-xxxx-xxxx-6b1530a0xxxx
        group: MARKET_GROUP
        file-extension: yaml
        refresh-enabled: true
  profiles:
    active: dev
3. 线程池配置类
3.1 基础配置类
// d:\develop\workspace\big-market\big-market-app\src\main\java\com\market\config\DynamicThreadPoolConfig.java
package com.market.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "thread-pool.market")
public class DynamicThreadPoolConfig {
    private int coreSize = 10;
    private int maxSize = 20;
    private int queueCapacity = 1000;
    private int keepAliveSeconds = 60;
    private String threadNamePrefix = "market-";
}
4. 自定义可调整容量队列
// d:\develop\workspace\big-market\big-market-app\src\main\java\com\market\config\ResizableCapacityQueue.java
package com.market.config;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ResizableCapacityQueue<E> implements BlockingQueue<E> {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();
    private final ArrayDeque<E> deque = new ArrayDeque<>();
    private volatile int capacity;

    public ResizableCapacityQueue(int initialCapacity) {
        this.capacity = initialCapacity;
    }

    // 核心方法:动态调整容量
    public void setCapacity(int newCapacity) {
        if (newCapacity <= 0) {
            throw new IllegalArgumentException("容量必须为正数");
        }
        lock.lock();
        try {
            int oldCapacity = this.capacity;
            this.capacity = newCapacity;
            // 如果新容量大于旧容量,唤醒等待的生产者
            if (newCapacity > oldCapacity) {
                notFull.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean offer(E e) {
        lock.lock();
        try {
            if (deque.size() == capacity) {
                return false;
            }
            deque.add(e);
            notEmpty.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void put(E e) throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (deque.size() == capacity) {
                notFull.await();
            }
            deque.add(e);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public E take() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (deque.isEmpty()) {
                notEmpty.await();
            }
            E e = deque.poll();
            notFull.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public int size() {
        lock.lock();
        try {
            return deque.size();
        } finally {
            lock.unlock();
        }
    }

    // 实现其他BlockingQueue接口方法...
    @Override
    public boolean add(E e) { return offer(e); }
    @Override
    public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { return offer(e); }
    @Override
    public E poll() { return deque.poll(); }
    @Override
    public E poll(long timeout, TimeUnit unit) throws InterruptedException { return deque.poll(); }
    @Override
    public E peek() { return deque.peek(); }
    @Override
    public int remainingCapacity() { return capacity - size(); }
    @Override
    public boolean remove(Object o) { return deque.remove(o); }
    @Override
    public boolean contains(Object o) { return deque.contains(o); }
    @Override
    public int drainTo(Collection<? super E> c) { return 0; }
    @Override
    public int drainTo(Collection<? super E> c, int maxElements) { return 0; }
    @Override
    public boolean isEmpty() { return deque.isEmpty(); }
    @Override
    public Object[] toArray() { return deque.toArray(); }
    @Override
    public <T> T[] toArray(T[] a) { return deque.toArray(a); }
    @Override
    public boolean containsAll(Collection<?> c) { return deque.containsAll(c); }
    @Override
    public boolean addAll(Collection<? extends E> c) { return deque.addAll(c); }
    @Override
    public boolean removeAll(Collection<?> c) { return deque.removeAll(c); }
    @Override
    public boolean retainAll(Collection<?> c) { return deque.retainAll(c); }
    @Override
    public void clear() { deque.clear(); }
}
5. 线程池工厂类
// d:\develop\workspace\big-market\big-market-app\src\main\java\com\market\config\ThreadPoolFactory.java
package com.market.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Configuration
public class ThreadPoolFactory {

    @Autowired
    private DynamicThreadPoolConfig threadPoolConfig;

    @Bean("marketTaskExecutor")
    public ThreadPoolTaskExecutor marketTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {
    @Override
    protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
        // 使用自定义可调整容量队列替代默认队列
        return new ResizableCapacityQueue<>(queueCapacity);
    }
};
        executor.setCorePoolSize(threadPoolConfig.getCoreSize());
        executor.setMaxPoolSize(threadPoolConfig.getMaxSize());
        executor.setQueueCapacity(threadPoolConfig.getQueueCapacity());
        executor.setKeepAliveSeconds(threadPoolConfig.getKeepAliveSeconds());
        executor.setThreadNamePrefix(threadPoolConfig.getThreadNamePrefix());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        log.info("初始化线程池完成: {}", threadPoolConfig);
        return executor;
    }
}
5. Nacos配置变更监听器
// d:\develop\workspace\big-market\big-market-app\src\main\java\com\market\listener\NacosThreadPoolListener.java
package com.market.listener;

import com.alibaba.cloud.nacos.NacosConfigReceivedEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.config.DynamicThreadPoolConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Map;

@Slf4j
@Component
public class NacosThreadPoolListener {

    @Autowired
    private DynamicThreadPoolConfig threadPoolConfig;

    @Autowired
    private ThreadPoolTaskExecutor marketTaskExecutor;

    @Autowired
    private ObjectMapper objectMapper;

    @EventListener(NacosConfigReceivedEvent.class)
    public void onConfigReceived(NacosConfigReceivedEvent event) {
        try {
            // 只处理线程池配置变更
            if ("big-market-app-threadpool.yaml".equals(event.getDataId())) {
                String configContent = event.getContent();
                if (StringUtils.hasText(configContent)) {
                    // 解析配置内容
                    Map<String, Object> configMap = objectMapper.readValue(configContent, Map.class);
                    Map<String, Integer> threadPoolMap = (Map<String, Integer>) configMap.get("thread-pool");
                    if (threadPoolMap != null && threadPoolMap.containsKey("market")) {
                        Map<String, Integer> marketConfig = (Map<String, Integer>) threadPoolMap.get("market");
                        updateThreadPoolConfig(marketConfig);
                    }
                }
            }
        } catch (Exception e) {
            log.error("处理线程池配置变更异常", e);
        }
    }

    private void updateThreadPoolConfig(Map<String, Integer> newConfig) {
        if (newConfig == null) return;

        // 更新配置类
        if (newConfig.containsKey("coreSize")) {
            int coreSize = newConfig.get("coreSize");
            threadPoolConfig.setCoreSize(coreSize);
            marketTaskExecutor.setCorePoolSize(coreSize);
            log.info("更新核心线程数: {}", coreSize);
        }

        if (newConfig.containsKey("maxSize")) {
            int maxSize = newConfig.get("maxSize");
            threadPoolConfig.setMaxSize(maxSize);
            marketTaskExecutor.setMaxPoolSize(maxSize);
            log.info("更新最大线程数: {}", maxSize);
        }

        if (newConfig.containsKey("queueCapacity")) {
            int queueCapacity = newConfig.get("queueCapacity");
            threadPoolConfig.setQueueCapacity(queueCapacity);
            // 获取实际工作队列并调整容量
            BlockingQueue<Runnable> queue = marketTaskExecutor.getThreadPoolExecutor().getQueue();
            if (queue instanceof ResizableCapacityQueue) {
                ((ResizableCapacityQueue<Runnable>) queue).setCapacity(queueCapacity);
                log.info("更新队列容量: {}", queueCapacity);
            } else {
                log.error("工作队列不是ResizableCapacityQueue类型,无法更新容量");
            }
        }
    }
}
6. Nacos配置内容 (big-market-app-threadpool.yaml)
thread-pool:
  market:
    coreSize: 15
    maxSize: 30
    queueCapacity: 2000
    keepAliveSeconds: 60
    threadNamePrefix: market-
7. 业务使用示例
// d:\develop\workspace\big-market\big-market-app\src\main\java\com\market\service\TaskService.java
package com.market.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TaskService {

    @Autowired
    private ThreadPoolTaskExecutor marketTaskExecutor;

    public void submitTask(Runnable task) {
        marketTaskExecutor.submit(() -> {
            try {
                task.run();
            } catch (Exception e) {
                log.error("任务执行异常", e);
            }
        });
    }

    // 获取当前线程池状态
    public String getThreadPoolStatus() {
        return String.format(
            "线程池状态: 核心线程数=%d, 最大线程数=%d, 活跃线程数=%d, 队列大小=%d, 已完成任务数=%d",
            marketTaskExecutor.getCorePoolSize(),
            marketTaskExecutor.getMaxPoolSize(),
            marketTaskExecutor.getActiveCount(),
            marketTaskExecutor.getThreadPoolExecutor().getQueue().size(),
            marketTaskExecutor.getThreadPoolExecutor().getCompletedTaskCount()
        );
    }
}
8. 实现说明
  1. 动态更新原理:通过监听Nacos配置变更事件,实时更新ThreadPoolTaskExecutor的核心参数和自定义队列容量
  2. 自定义队列特性:ResizableCapacityQueue通过ReentrantLock和Condition实现线程安全的容量动态调整,支持队列满时的生产者等待和队列空时的消费者等待
  3. 线程安全保障
    • ThreadPoolTaskExecutor的setter方法内部使用ReentrantLock保证线程安全
    • 自定义队列通过显式锁机制确保容量调整和元素操作的线程安全
  4. 配置热更新:无需重启服务,配置变更后5秒内即可生效,其中队列容量更新通过反射调用自定义队列的setCapacity方法
  5. 状态监控:提供getThreadPoolStatus方法监控线程池实时状态,包括队列当前大小
  6. 降级策略:使用CallerRunsPolicy拒绝策略,确保任务不丢失

通过以上实现,可在Nacos控制台动态调整线程池参数,包括核心线程数、最大线程数和队列容量,应对不同时段的流量变化,特别适合大营销项目中的秒杀、拼团等高并发场景。

9. 注意事项
  1. 自定义队列需完整实现BlockingQueue接口的所有方法,确保线程池正常工作
  2. 队列容量调整时需注意:缩小容量可能导致部分任务被拒绝,建议配合适当的监控告警
  3. 生产环境使用时,建议为ResizableCapacityQueue添加JMX监控支持,便于运维监控队列容量变化
  4. 若需频繁调整队列容量,建议限制调整频率(如1分钟内不超过3次),避免频繁唤醒等待线程影响性能
  5. 动态更新原理:通过监听Nacos配置变更事件,实时更新ThreadPoolTaskExecutor的核心参数
  6. 线程安全保障:ThreadPoolTaskExecutor的setter方法内部使用ReentrantLock保证线程安全
  7. 配置热更新:无需重启服务,配置变更后5秒内即可生效
  8. 状态监控:提供getThreadPoolStatus方法监控线程池实时状态
  9. 降级策略:使用CallerRunsPolicy拒绝策略,确保任务不丢失

通过以上实现,可在Nacos控制台动态调整线程池参数,应对不同时段的流量变化,特别适合大营销项目中的秒杀、拼团等高并发场景。

你可能感兴趣的:(Nacos动态线程池完整实现方案)