多消费者(多线程)对MNS的使用

简介: 多消费者(多线程)对MNS的使用 背景 在阿里云MNS消费者的使用中,阿里云提供了使用 [消息服务 > 最佳实践 > 长轮询](https://help.aliyun.com/document_detail/34478.html?spm=a2c4g.11174283.3.3.AqOdUy#h2-u89E3u51B3u65B9u6848) 的代码和说明,在解决方案中阿里云这么说道 在开了上百个线程同时访问的情况下,如果队列里已经没有消息了,那么其实不需要上百个线程都同时挂LongPolling。

多消费者(多线程)对MNS的使用

背景

在阿里云MNS消费者的使用中,阿里云提供了使用 消息服务-最佳实践-长轮询
的代码和说明,在解决方案中阿里云这么说道

在开了上百个线程同时访问的情况下,如果队列里已经没有消息了,那么其实不需要上百个线程都同时挂LongPolling。只需要有1-N个线程挂LongPolling就足够了。挂LongPolling的线程在发现队列里有消息时,可以唤醒其他线程一起来取消息以达到快速响应的目的
Receiver内部做了LongPolling的排他机制,只要有一个线程在做LongPolling,那么其他线程只需要Wait就可以了。 —— [解决方案]

但是如何启动1-N个线程,同时产生多个消费者,并没有给出说明,阿里云官方提供的demo中是使用在main方法中启用:

CloudAccount account = new CloudAccount("ACCESS_ID", "ACCESS_KEY", "ENDPOINT");
        sMNSClient = account.getMNSClient();
        sMNSClient.getQueueRef("TestQueue").delete();
        
        sMNSClient.getQueueRef("TestQueue").create();

        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                WorkerFunc(1);
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                WorkerFunc(2);
            }
        });
        Thread thread3 = new Thread(new Runnable() {
            public void run() {
                WorkerFunc(3);
            }
        }); 

这里我提供一种比较好的方法,可以利用spring IOC容器的依赖注入,来管理和启动多个消费者(多线程)。

方法展示

Spring会通过依赖注入的方式,来管理关联对象的生命周期,所以我们可以将消费者的产生管理,都由Spring IOC容器代劳,也就是说,我把消费者创建的控制权都交给Spring容器。方法如下

@Component
public class NormalProcessComponent {
    
     private static Logger log = LoggerFactory.getLogger(NormalProcessComponent.class);
    private static ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(30);
    
    public NormalProcessComponent(){
         for(int i = 0; i < 50; i++){
             threadPool.execute(new Runnable() {
                
                @Override
                public void run() {
                    try {
                        process();
                    } catch (Exception e) {
                        
                        e.printStackTrace();
                    }
                    
                }
            });
         }

    }
    
     public void process() throws Exception {
              //使用阿里云官方提供的方法
            MessageReceiver receiver = new MessageReceiver(workerId, sMNSClient, "TestQueue");
            while (true) {
                Message message = receiver.receiveMessage();
                  try {
                    //取出Que中的信息
                     result = message .getMessageBodyAsString(); 
                     JSONObject params = JSONObject.parseObject(result);
                    if(params!=null){
                       //处理数据的方法                        
                    } else {
                        log.info("取出的数据为空!");
                         Thread.sleep(Constant.SLEEP_SECONDS);
                    }
                    
                 
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("fail to sleep"+message);
                    break;
                }
            }
        }
}

我们将消费者的产生方法,在类中的构造函数中定义,使用一个固定大小的线程池,来管理消费者(线程),同时加上Component注解,在项目启动时,Spring 的就会实例化这个类,注入到容器中,这个时候构造方法中的,多个消费者就会启动开始工作。

拓展

  • 阿里云官方MessageReceiver的解析 :长连接轮询,以及死锁和线程安全性问题的避免
  • 消费者的监控 :观察消费者的数量,避免消费者全部死亡,造成队列积压;

阿里云官方MessageReceiver的解析

在阿里云MNS消费者的使用中,阿里云提供了使用 消息服务-最佳实践-长轮询 ,官方已经提供了源代码和详细说明,我在这里就不贴代码了,主要说明其中的原理。
avatar

在MessageReceiver中,官方定义了一个

static final Map<String, Object> sLockObjMap

从而保证了,无论new出多少个MessageReceiver,都是从同一个Map,取出的lockObj。在使用lockObj中,均使用同步锁synchronized,从而实现了LongPolling的排他机制,只有一个线程在做LongPolling,其他线程都会Wait。避免了上百个线程同时访问MNS Server,一个Group只会产生,一条长连接进行长轮询。
可以将图中的Group,比作一台台服务器,而里面的多个Consumer,实际就是启动的多个消费线程。

消费者的监控

在上面代码中,使用了一个固定大小的线程池来管理多个线程(消费者),但是一旦子线程死亡,这个线程(消费者),并不会重启,这种情况就会产生队列积压。产生线程死亡一定是不正常,程序中的Bug存在。比如,有异常没有捕获到,或者在子线程中将异常throw出,就会使当前子线程死亡掉。这种情况一定是会有的,因为没有人写出的代码是完美无缺的,程序员只能尽可能避免bug的产生,所以我们需要用完善的日志和监控来完善,我们的项目。

这里我们可以利用监控线程池中的存活线程数量从而来,进行报警。

      //当消费者低于一定阈值触发报警
       if(threadPool.getActiveCount()<threshold){
           //报警       
        }

可以将这个封装成一个API接口,通过监控这个API来进行报警。

相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
3月前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
42 1
|
7月前
|
安全 API C++
c++生产者和消费者线程循环
线程安全-生产者消费者模型
74 1
|
3月前
|
Java C++
线程池-手写线程池C++11版本(生产者-消费者模型)
线程池-手写线程池C++11版本(生产者-消费者模型)
61 0
|
3月前
|
Java Linux C语言
线程池-手写线程池Linux C简单版本(生产者-消费者模型)
线程池-手写线程池Linux C简单版本(生产者-消费者模型)
42 0
|
8月前
|
Java
经典 生产者-消费者线程【操作系统】
经典 生产者-消费者线程【操作系统】
39 0
|
8月前
|
存储 安全 Java
Java中多线程同步问题、生产者与消费者、守护线程和volatile关键字(附带相关面试题)
1.多线程同步问题(关键字Synchronized),2. Object线程的等待与唤醒方法,3.模拟生产者与消费者,4.守护线程,5.volatile关键字
47 0
|
8月前
|
存储 安全 算法
【Java|多线程与高并发】阻塞队列以及生产者-消费者模型
阻塞队列(BlockingQueue)常用于多线程编程中,可以实现线程之间的同步和协作。它可以用来解决生产者-消费者问题,其中生产者线程将元素插入队列,消费者线程从队列中获取元素,它们之间通过阻塞队列进行协调。
|
安全 数据处理
线程中的生产者和消费者模式
线程中的生产者和消费者模式
100 0
线程中的生产者和消费者模式
|
Java
Java 线程 案例:生产者与消费者
Java 线程 案例:生产者与消费者
130 0
Java 线程 案例:生产者与消费者
|
监控 Java 数据挖掘
Java多线程(三)、线程的通信、wait(),notify(),notifyAll()、生产者/消费者问题、创建线程的方式三:实现Callable接口、创建线程的方式四:使用线程池
Java多线程(三)、线程的通信、wait(),notify(),notifyAll()、生产者/消费者问题、创建线程的方式三:实现Callable接口、创建线程的方式四:使用线程池
Java多线程(三)、线程的通信、wait(),notify(),notifyAll()、生产者/消费者问题、创建线程的方式三:实现Callable接口、创建线程的方式四:使用线程池