有序线程池(可根据订单号hash到同一个线程处理,适用于消息消费)

package com.dingxy.database.util;

import com.google.common.hash.Hashing;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: 
 * @since: 2024/12/17 下午3:45
 * @description:自定义有序线程池
 * @version: 1.0 , Copyright (c) 2023 by ZTE Co.
 */
public class OrderedThreadPoolExecutor {

    private final InnerTaskWorker[] taskWorkers;
    private final ThreadFactory threadFactory;
    public OrderedThreadPoolExecutor(int concurrency,ThreadFactory threadFactory){
        this.taskWorkers = new InnerTaskWorker[concurrency];
        this.threadFactory = threadFactory;
        this.init(concurrency);
    }

    private void init(int concurrency){
        this.initFillRunTasks(concurrency);
        this.addShutdownHook();
    }

    /**
     * 初始化填充并行任务
     * @param taskCount 任务数,即并发度
     */
    private void initFillRunTasks(int taskCount) {
        for (int i = 0; i < taskCount; i++) {
            this.taskWorkers[i] = new InnerTaskWorker(this.threadFactory);
        }
    }

    /**
     * 设置jvm退出的钩子,响应中断、释放资源
     * 1. 不再接受新任务
     * 2. 停止内部线程池
     */
    private void addShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(() ->
                Arrays.asList(this.taskWorkers).forEach(InnerTaskWorker::stopWorker)));
    }

    /**
     * 根据提供的hashKey,执行有序的任务
     * @param hashKey 分区标识
     * @param task    任务
     */
    public void executeOrdered(int hashKey, Runnable task) {
        int i = hashKey % taskWorkers.length;
        this.taskWorkers[i].addWorker(task);
    }


    private static final class InnerTaskWorker{
        private final  SynchronousQueue<Runnable> sq;
        private final  AtomicInteger threadState;
        private final ThreadFactory threadFactory;
        private volatile  Thread command;

        private InnerTaskWorker(ThreadFactory threadFactory){
           this.sq = new SynchronousQueue<>();
           this.threadState = new AtomicInteger(1);
           this.threadFactory = threadFactory ;
           this.init();
        }

        private void  init(){
            this.startWorker();
        }

        private Boolean isRunning(){
            return  this.threadState.get() == InnerThreadState.RUNNING;
        }

        private Boolean isTerminated(){
            return  this.threadState.get() == InnerThreadState.TERMINATED;
        }

        private void startWorker(){
            this.command = threadFactory.newThread(()->{
                while (this.isRunning()){
                    try {
                        sq.take().run();
                    } catch (InterruptedException e) {
                        if (this.isTerminated()){
                            throw new RuntimeException(e);
                        }
                    }catch (RuntimeException t){
                        this.handleWorkerException();
                        throw  t;
                    }
                }
            });
            this.command.start();
        }
        /**
         * 处理任务异常时,继续有工作线程执行任务
         */
        private void handleWorkerException() {
            this.threadState.set(InnerThreadState.EXCEPTED);
            // 通过cas方式来启动新线程
            // 这个判断不是脱裤子放屁,因为threadState.set()与startWorker()并不是原子操作
            if (this.threadState.compareAndSet(InnerThreadState.EXCEPTED, InnerThreadState.RUNNING)) {
                this.startWorker();
            }
        }

        private void addWorker(Runnable r){
            try {
                sq.put(r);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        private void stopWorker(){
            this.threadState.set(InnerThreadState.TERMINATED);
            this.command.interrupt();

            try {
                this.command.join();
            } catch (InterruptedException e) {
                System.out.println("abcdf");
            }
        }

    }

    public static void main(String[] args) {

        OrderedThreadPoolExecutor orderedExecutor = new OrderedThreadPoolExecutor(64,new CustomThreadFactory("order-"));

        Runnable task = () -> {
            if (new Random().nextBoolean()) {
                try {
                    System.out.println("hhahah");
                    Thread.sleep(20L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 模拟偶尔业务代码抛出异常情况
                if (new Random().nextBoolean()) {
                    throw new RuntimeException("Test exception");
                }
            }
        };

        StringBuilder log = new StringBuilder();

        for (int i = 0; i < 1000; i++) {
            try {
                String orderId = "o_" + (100_000_000 + i);
                int hash = Math.abs(Hashing.murmur3_32().hashBytes(orderId.getBytes()).asInt());
                orderedExecutor.executeOrdered(hash, task);
            } catch (Exception ignore) {
                // 忽略模拟的业务代码异常
            }
        }

        InnerTaskWorker[] tws = orderedExecutor.taskWorkers;
        for (int i = 0; i < tws.length; i++) {
            log.append(tws[i].command.getName())
                    .append(" ==> ")
                    .append(tws[i].command.getState())
                    .append("\n");
        }

        System.out.println(log);
    }

    /* Started by AICoder, pid:6a35441173024a02956093915a9e1bca */

    public static final  class CustomThreadFactory implements ThreadFactory {

        private final String namePrefix;
        private final AtomicInteger threadId = new AtomicInteger(0);

        public CustomThreadFactory(String namePrefix) {
            this.namePrefix = namePrefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, namePrefix + "-thread-" + threadId.getAndIncrement());
            return t;
        }
    }
    /* Ended by AICoder, pid:6a35441173024a02956093915a9e1bca */

    /**
     * 内部线程状态码
     */
    private static final class InnerThreadState {
        /**
         * 正常运行
         */
        private static final int RUNNING = 1;
        /**
         * 异常停止
         */
        private static final int EXCEPTED = 2;
        /**
         * 正常结束
         */
        private static final int TERMINATED = 3;
    }
}

<think>嗯,用户问的是关于根据key哈希到各个线程消息提交时各个分区顺序提交的问题。首先,我需要确定用户的具体场景。可能是在分布式系统或消息队列中处理数据,比如Kafka这样的系统。用户提到的key哈希到线程,可能涉及到数据分区和并行处理。 接下来,我需要理解key的作用。通常,通过key的哈希值来决定数据应该发送到哪个分区,这样可以保证同一个key的数据总是落到同一个分区,维持顺序。例如,在Kafka生产者中,默认的分区策略就是基于key的哈希值选择分区。这样做的目的是为了保持消息的顺序性和负载均衡。 然后,用户提到各个分区顺序提交消息。这里可能需要解释分区的顺序性是如何保证的。每个分区内部的消息有序的,但不同分区之间的顺序无法保证。所以,如果应用需要保证相同key的消息顺序,就必须让它们进入同一个分区,这样消费线程处理时就能按顺序消费。 还需要考虑线程和分区的映射关系。哈希算法如何将key分配到不同的线程或分区。常见的哈希算法如一致性哈希,或者简单的取模运算。但需要注意哈希算法的选择可能会影响负载均衡,如果某些key特别多,可能导致分区不均匀。 另外,消息提交的顺序提交可能涉及到生产者的发送机制。比如,生产者可以按顺序发送消息到每个分区,或者异步发送。如果顺序很重要,可能需要同步发送,或者使用回调确保消息顺序提交。 可能存在的挑战包括:哈希冲突导致分区不均匀,如何动态调整分区数量而不影响现有数据顺序,以及故障处理时的数据一致性。需要权衡一致性和可用性,根据具体场景选择合适的策略。 用户可能没有明确说出的是,他们可能遇到了性能问题或者顺序错乱的问题,需要优化分区策略。或者他们正在设计一个新系统,需要了解如何合理分配消息到分区,同时保持顺序和高效处理。 总结来说,回答需要涵盖分区策略、哈希算法、顺序保证、线程与分区的映射、提交机制以及潜在的问题和解决方案。确保用户理解如何通过key的哈希实现负载均衡和顺序性,同时注意实际应用中的注意事项。</think>好的,我将逐步解释基于key哈希的分区提交机制: 1. **核心目标** - 实现负载均衡:通过哈希将数据分散到不同线程/分区 - 保证顺序性:相同key的数据必须进入同一分区 - 提升吞吐量:并行处理不同分区的数据 2. **哈希映射机制** - 计算过程:$$ \text{partition\_id} = \text{hash(key)} \ \% \ \text{partition\_num} $$ - 常用哈希算法: - MurmurHash3 (Kafka默认) - CRC32 - MD5截断 - 特性要求: - 低碰撞率 - 快速计算 - 均匀分布 3. **顺序提交实现** ```java // 伪代码示例 class Producer { Map<Partition, Queue<Message>> partitionQueues; void send(Message msg) { int partition = hash(msg.key) % partitions.size(); synchronized(partitionQueues[partition]) { // 分区级别锁 partitionQueues[partition].add(msg); } } void flush() { for (Partition p : partitions) { // 按分区顺序提交 sendToBroker(p, partitionQueues[p]); } } } ``` 4. **关键设计考虑** - 线程安全: - 每个分区维护独立队列 - 使用细粒度锁(分区级锁而非全局锁) - 顺序保证: - 单分区内FIFO处理 - 跨分区并行处理 - 故障恢复: - 分区级别重试机制 - 保存最后提交位移(offset) 5. **性能优化手段** - 批处理提交: $$ \text{提交条件} = \begin{cases} \text{batch.size} \geq 16KB \\ \text{linger.ms} \geq 100ms \end{cases} $$ - 内存管理: - 每个分区独立内存缓冲区 - 背压机制(backpressure)防止OOM 6. **典型应用场景** - 消息队列(Kafka/RocketMQ) - 流处理(Spark Streaming/Flink) - 分布式计算(MapReduce) **注意事项** 1. 热点问题:当某个key数据量过大时,可能造成分区倾斜 - 解决方案:添加随机后缀(salt)到key 2. 扩容影响:增减分区会导致哈希结果变化 - 应对方案:使用一致性哈希算法 3. 顺序严格性: - 严格顺序:牺牲吞吐量(线程消费) - 宽松顺序:允许分区内有序+并行处理 需要
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值