Astrotrain概述

Astrotrain是基于阿里巴巴开源项目RocketMQ进行封装的分布式消息中间件系统,提供集群环境下的消息生产和消费功能。

RocketMQ介绍

RocketMQ的物理部署结构

  • Name Server 是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。所有的主题和broker节点信息都由Name Server进行维护。

  • Broker 是主要的功能单元,处理主题的存储和消费逻辑,Broker会定时同步所有信息至Name Server。

  • 一类Producer的集合名称,这类Producer通常发送一类消息,且发送逻辑一致。
  • 一类Consumer的集合名称,这类Consumer通常消费一类消息,且消费逻辑一致。Consumer群组内多应用之间消息消费是竞争关系,Consumer群组之间是共享消费,这点非常重要。

RocketMQ存储特点

  • Broker上绑定具体的Topic。
  • Topic下有多个物理存储队列(Queue),所有存储队列都会存储消息,一个消息只会存储一份。Topic可以分布在不同Broker上。
  • 存储队列的选择决定了消费的特性,如果只读写一个队列,那么消费就是顺序的了,否则会是无序的。

消费者Push和Pull的区别

  • Push模式下的消息是由事件触发,有消息到达时监听器会被调用(MessageListener)。

  • Pull模式下的消息可以由事件触发,也可以应用主动去拉取消息,没有消息可拉取时返回空。

RocketMQ设计文档

RocketMQ_design.pdf

Astrotrain介绍

Astrotrain-Client是对RocketMQ的Producer和Consumer的封装,集中解决了RocketMQ诸多配置信息和使用特性,针对特定需求可以进行二次开发来进行扩展。

Astrotrain的整体结构

  • Basic Component是Astrotrain对RocketMQ的基础封装,里面包含的生产者和消费者处理消息的逻辑,同时处理了RocketMQ的很多配置信息。

  • JDBC Message Component是消息事务的功能模块,依赖Basic Component的功能实现。

  • Astrotrain底层依赖RocketMQ提供消息服务。

  • 业务层只面对Astrotrain暴露的服务。

Astrotrain的逻辑结构

  • ATClient定义最基础的功能,包括客户端的启动、初始化、关闭的定义。

  • Pipe是生产者和消费者公共部分的抽象,与具体的Topic进行绑定。

  • ATProducer是对生产者的封装,提供基础的消息发送服务。
  • ATConsumer是对消费者的封装,提供基础的消息消费服务(没有具体的Pipe实现,因为消费是针对Listener的)。

  • ATMessage是对外提供的消息体,目前只提供StringMessage,ObjectMessage,后续再进行扩展。

Astrotrain使用示例

对于Astrotrain的使用主要是基于astrotrain-client来实现,目前有两个版本可供使用,1.0和1.0.1,他们之间的区别在于后者提供了一个批量消费的接口,其余的相同,所以以1.0.1的版本为例说。

注意:下面列出的所有配置文件astrotrain-client会默认从ClassPath中进行读取,不需要显式指定。

版本信息

  1. astrotrain-client-1.0.2版本,2015/02/05
    1、开放了tag注册,消费端新增 subscribe(topic,tags,listener) 方法
    2、去除了对配置文件的依赖(astrotrain-produce/astrotrain-consumer/astrotrain),使用Spring注入风格注入属性,生产者对应DefaultATProducer,消费者对应DefaultATPushConsumer这两个实体
    3、增加BytesMessage支持字节数据消息
    4、在消费端可以正常获取发送端设置的key和tag熟悉,通过ATMessage.getProperty() 方法,键值与发送端相同

Maven依赖

maven
<dependency>
    <groupId>com.zj</groupId>
    <artifactId>astrotrain-client</artifactId>
    <version>1.0.1</version>
</dependency>

生产端(Producer)

  1. 准备资源配置文件astrotrain-producer.properties,生产者配置

    astrotrain-producer.properties
    #生产者群组名称
    astrotrain.group.name=PleaseRename
    #应用实例名称
    astrotrain.instance.name=ProducerAT
    #namesrv地址,多个之间以分号 ; 分隔
    astrotrain.namesrv.address=10.10.110.51:9876
  2. 准备资源文件astrotrain.properties,应用配置

    astrotrain.properties
    #应用标志符
    astrotrain.appId=app1
  3. Java代码,准备POJO。

    Order.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    package com.zj.astrotrain.demo;
    import java.io.Serializable;
    import java.util.List;
    public class Order implements Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 1L;
         
        private long id;
        private String orderId;
        private String cardNo;
        private List<String> payments;
         
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
        public String getOrderId() {
            return orderId;
        }
        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }
        public String getCardNo() {
            return cardNo;
        }
        public void setCardNo(String cardNo) {
            this.cardNo = cardNo;
        }
         
        public List<String> getPayments() {
            return payments;
        }
        public void setPayments(List<String> payments) {
            this.payments = payments;
        }
         
        public String toString(){
            return "Order [id=" this.id + ",orderId="
                    this.orderId + ",cardNo=" this.cardNo
                    ",payments=" this.payments == null null this.payments.size() + "]";
        }
    }
  4. 生产者代码

    ProduerDemon.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    package com.zj.astrotrain.demo;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import com.alibaba.fastjson.JSON;
    import com.zj.astrotrain.client.ATMessage;
    import com.zj.astrotrain.client.ATProducer;
    import com.zj.astrotrain.client.exceptions.ATException;
    import com.zj.astrotrain.client.message.ObjectMessage;
    import com.zj.astrotrain.client.message.StringMessage;
    import com.zj.astrotrain.client.producer.DefaultATProducer;
    /**
     * 生成者示例
     * <pre>需要在 ClassPath 路径下准备 astrotrain-producer.properties 具体配置信息参考 src/main/resources下的配置</pre>
     *
     *
     */
    public class ProducerDemon {
        private DefaultATProducer atProducer;
     
        public ProducerDemon() {
     
        }
     
        public void setUp() {
            this.atProducer = new DefaultATProducer();
            try {
                this.atProducer.start();
            catch (Exception e) {
                e.printStackTrace();
            }
        }
     
        /**
         * 发送字符类型的消息
         */
        public void doStringMessage() {
            //获取一个生成者通道,指定Topic
            ATProducer producer = this.atProducer.createProducer("demo");
            DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            for(int i = 0; i < 100; i++){
                Order order = new Order();
                order.setId(i);
                order.setOrderId(format.format(new Date()) + "_" + i);
                order.setCardNo("6221202000111112222");
                //新建一个StringMessage
                StringMessage msg = new StringMessage(JSON.toJSONString(order));
                //为消息设置一个业务标识符,最好是唯一的,方便在调试程序时进行跟踪,可选属性.
                msg.setProperty(ATMessage.MSG_KEYS, order.getOrderId());
                try {
                    //进行消息发送,不抛异常的都是正常发送.除非服务端程序Crash,不然不会丢失消息
                    producer.send(msg);
                catch (ATException e) {
                    e.printStackTrace();
                }
            }
        }
     
        /**
         * 发送对象类型的消息
         */
        public void doObjectMessage() {
            //使用新的主题创建生产者通道
            ATProducer producer = this.atProducer.createProducer("demo");
            DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            for(int i = 0; i< 100; i++) {
                //新建一个ObjectMessage
                ObjectMessage msg = new ObjectMessage();
                Order order = new Order();
                order.setId(i);
                order.setOrderId(format.format(new Date()) + "_" + i);
                order.setCardNo("6221202000111112222" + i);
                List<String> payments = new ArrayList<String>();
                payments.add("payment" + i);
                order.setPayments(payments);
                //设置对象
                msg.putObject(order);
                //为消息设置一个业务标识符,最好是唯一的,方便在调试程序时进行跟踪,可选属性.
                msg.setProperty(ATMessage.MSG_KEYS, order.getOrderId());
                try {
                    //进行消息发送,不抛异常的都是正常发送.除非服务端程序Crash,不然不会丢失消息
                    producer.send(msg);
                catch (ATException e) {
                    e.printStackTrace();
                }
            }
        }
     
        public void shutdown() {
            if(this.atProducer != null){
                this.atProducer.shutdown();
            }
        }
     
        public static void main(String[] args) {
            ProducerDemon demon = new ProducerDemon();
            demon.setUp();
            demon.doStringMessage();
            demon.doObjectMessage();
            demon.shutdown();
        }
     
    }

消费端(Consumer)

  1. 准备资源文件astrotrain-consumer.properties,消费者配置。

    astrotrain-consumer.properties
    #消费者群组名称,与生产者群组没有关联
    astrotrain.group.name=PleaseRename
    #消费者示例名称
    astrotrain.instance.name=ConsumerATbatch
    #namesrv的地址,多个以分号 ; 分隔
    astrotrain.namesrv.address=10.10.110.51:9876
    #消费模式,CLUSTERING and BROADCASTING, default is CLUSTERING
    astrotrain.consumer.messageModel=CLUSTERING
    #消费者启动时从那个位置开始消费
    astrotrain.consumer.consumeFromWhere=CONSUME_FROM_FIRST_OFFSET
    #消费者线程最小数
    astrotrain.consumer.consumeThreadMin=10
    #消费者线程最大数
    astrotrain.consumer.consumeThreadMax=20
    #单次消费时一次性消费多少条消息,批量消费接口才有用,可选配置。
    #astrotrain.consumer.batchMaxSize=30
    #消费者去broker拉取消息时,一次拉取多少条。可选配置。
    #astrotrain.consumer.pullBatchSize=100
    #每次拉取消息的间隔,默认为0,可选配置/
    #astrotrain.consumer.pullInterval=1000
  2. 准备资源文件astrotrain.properties,应用配置

    astrotrain.properties
    #应用标志符
    astrotrain.appId=app2
  3. 消费者,单个消息消费

    ConsumerDemon.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    package com.zj.astrotrain.demo;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.rocketmq.client.exception.MQClientException;
    import com.zj.astrotrain.client.ATMessage;
    import com.zj.astrotrain.client.MessageListener;
    import com.zj.astrotrain.client.consumer.DefaultATPushConsumer;
    import com.zj.astrotrain.client.message.ObjectMessage;
    import com.zj.astrotrain.client.message.StringMessage;
    /**
     * 单个消息消费
     *
     *
     */
    public class ConsumerDemon {
        private DefaultATPushConsumer atPushConsumer;
         
        public ConsumerDemon() {
             
        }
         
        public void setUp() {
            this.atPushConsumer = new DefaultATPushConsumer();
            try {
                //订阅必须在start之前
                this.atPushConsumer.subscribe("demo"new DemonMessageListener());
            catch (MQClientException e) {
                e.printStackTrace();
            }
        }
         
        public void start() {
            if(this.atPushConsumer != null) {
                try {
                    this.atPushConsumer.start();
                    System.in.read();//按任意键退出
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
         
        public void shutdown() {
            if(this.atPushConsumer != null){
                this.atPushConsumer.shutdown();
            }
        }
         
        public static void main(String[] args) {
            ConsumerDemon demon = new ConsumerDemon();
            demon.setUp();
            demon.start();
            demon.shutdown();
        }
        //单个消息监听接口
        public class DemonMessageListener implements MessageListener {
            @Override
            public void onMessage(ATMessage message) {
                try {
                    if(message instanceof StringMessage){
                        StringMessage msg = (StringMessage) message;
                        Order order = JSON.parseObject(msg.getMsg(), Order.class);
                        System.out.println(order.getCardNo());
                    else if(message instanceof ObjectMessage) {
                        ObjectMessage msg = (ObjectMessage) message;
                        Order order = (Order) msg.getObject();
                        System.out.println(order.getCardNo());
                    }
                catch(Exception e) {
                    e.printStackTrace();
                }
            }
             
        }
    }
  4. 消费者批量消费

    ConsumerBatchDemon
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    package com.zj.astrotrain.demo;
    import java.util.List;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.rocketmq.client.exception.MQClientException;
    import com.zj.astrotrain.client.ATMessage;
    import com.zj.astrotrain.client.ConcurrentlyMessageListener;
    import com.zj.astrotrain.client.consumer.DefaultATPushConsumer;
    import com.zj.astrotrain.client.message.ObjectMessage;
    import com.zj.astrotrain.client.message.StringMessage;
    /**
     * 消费者批量消费示例
     * <pre>需要在 ClassPath 路径下准备 astrotrain-consumer.properties 具体配置信息参考 src/main/resources下的配置</pre>
     *
     *
     */
    public class ConsumerBatchDemon {
        private DefaultATPushConsumer atPushConsumer;
         
        public ConsumerBatchDemon() {
             
        }
         
        public void setUp() {
            this.atPushConsumer = new DefaultATPushConsumer();
            try {
                //订阅必须在start之前
                this.atPushConsumer.subscribe("demo"new DemonConcurrentlyMessageListener());
            catch (MQClientException e) {
                e.printStackTrace();
            }
        }
         
        public void start() {
            if(this.atPushConsumer != null) {
                try {
                    this.atPushConsumer.start();
                    System.in.read();//按任意键退出
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
         
        public void shutdown() {
            if(this.atPushConsumer != null){
                this.atPushConsumer.shutdown();
            }
        }
         
        public static void main(String[] args) {
            ConsumerBatchDemon demon = new ConsumerBatchDemon();
            demon.setUp();
            demon.start();
            demon.shutdown();
        }
         
        /**
         * 消息监听
         *
         *
         */
        public class DemonConcurrentlyMessageListener implements ConcurrentlyMessageListener{
            @Override
            public void onMessage(List<ATMessage> msgs) {
                for(ATMessage message : msgs) {
                    try {
                        if(message instanceof StringMessage){
                            StringMessage msg = (StringMessage) message;
                            Order order = JSON.parseObject(msg.getMsg(), Order.class);
                            System.out.println(order.getCardNo());
                        else if(message instanceof ObjectMessage) {
                            ObjectMessage msg = (ObjectMessage) message;
                            Order order = (Order) msg.getObject();
                            System.out.println(order.getCardNo());
                        }
                    catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }
             
        }
         
    }

注意事项

    1. 订阅时不要加上tag,以*注册所有tag,然后再做筛选,订阅时附上tag会导致先注册的相同topic不能正常消费
    2. 在开发环境里Topic和SubGroup都是自动创建的,生产上是需要手动创建的,所以上线之前一定要检查对应的主题和订阅组是否已创建

RocketMQ 笔记-转的更多相关文章

  1. [分布式学习]消息队列之rocketmq笔记

    文档地址 RocketMQ架构 哔哩哔哩上的视频 mq有很多,近期买了<分布式消息中间件实践>这本书,学习关于mq的相关知识.mq大致有有4个功能: 异步处理.比如业务端需要给用户发送邮件 ...

  2. RocketMQ 笔记

    Queue Topic和Queue是1对多的关系,一个Topic下可以包含多个Queue,主要用于负载均衡.发送消息时,用户只指定Topic,Producer会根据Topic的路由信息选择具体发到哪个 ...

  3. ROCKETMQ源码分析笔记1:tools

    rocketmq源码解析笔记 大家好,先安利一下自己,本人男,35岁,已婚.目前就职于小资生活(北京),职位是开发总监. 姓名DaneBrown 好了.我保证本文绝不会太监!转载时请附上以上安利信息. ...

  4. RocketMQ 简单梳理 及 集群部署笔记【转】

    一.RocketMQ 基础知识介绍Apache RocketMQ是阿里开源的一款高性能.高吞吐量.队列模型的消息中间件的分布式消息中间件. 上图是一个典型的消息中间件收发消息的模型,RocketMQ也 ...

  5. RocketMQ学习笔记(16)----RocketMQ搭建双主双从(异步复制)集群

    1. 修改RocketMQ默认启动端口 由于只有两台机器,部署双主双从需要四个节点,所以只能修改rocketmq的默认启动端口,从官网下载rocketmq的source文件,解压后使用idea打开,全 ...

  6. RocketMQ学习笔记(15)----RocketMQ的消息模式

    在前面学习ActiveMQ时,看到ActiveMQ可以是队列消息模式,也可以是订阅发布模式. 同样,在RocketMQ中,也存在两种消息模式,即是集群消费模式和广播消费模式. 1. 集群消费模式 跟A ...

  7. RocketMQ学习笔记(13)----RocketMQ的Consumer消息重试

    1. 概念 Producer端重试: 生产者端的消息失败,也就是Producer往MQ上发消息没有发送成功,比如网络抖动导致生产者发送消息到MQ失败. 这种消息失败重试我们可以手动设置发送失败重试的次 ...

  8. RocketMQ学习笔记(14)----RocketMQ的去重策略

    1. Exactly Only Once (1). 发送消息阶段,不允许发送重复的消息 (2). 消费消息阶段,不允许消费重复的消息. 只有以上两个条件都满足情况下,才能认为消息是“Exactly O ...

  9. RocketMQ学习笔记(4)----RocketMQ搭建双Master集群

    前面已经学习了RockeMQ的四种集群方式,接下来就来搭建一个双Master(2m)的集群环境. 1. 双Master服务器环境 序号 ip 用户名 密码 角色 模式 (1) 47.105.145.1 ...

随机推荐

  1. Problem H. The Fence 通过取余判重,求得某个区间的某些个数为某个数的倍数。

    /** 题目:Problem H. The Fence 链接:https://vjudge.net/problem/Gym-101090H 题意:给定一个字符串,只有0或者1: 问:假如两个不同的1之 ...

  2. Crontab使用方式

    Liunx系统的定时任务需要Crontab来完成 一.添加 添加定时脚本 crontab -e 或者直接编辑/etc/crontab文件进行任务添加 vim /etc/crontab 二.格式 三.举 ...

  3. iostat -d -k -x 1 10

    iostat命令详解 转载 2011年08月03日 14:13:58 标签:磁盘 /扩展 /user 99809 iostat iostat用于输出CPU和磁盘I/O相关的统计信息. 命令格式: io ...

  4. 【转】Web前端开发:为何选择MVVM而非MVC

    在Web中充斥着所谓的MVC框架,而在我看来,因为一些关键性的技术原因,MVC在Web前端开发中根本无法使用(对的,是无法,而不是不该) 在Web中充斥着所谓的MVC框架,而在我看来,因为一些关键性的 ...

  5. apache+svn No installed service name 'Apache2' 【转载】

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://zys0597.blogbus.com/logs/32763815.html 问题:第一次在winxp下   安装apac ...

  6. spark使用KryoRegistrator java代码示例

    转载引用自:http://www.cnblogs.com/tovin/p/3833985.html 最近在使用spark开发过程中发现当数据量很大时,如果cache数据将消耗很多的内存.为了减少内存的 ...

  7. ChemDraw是这样预测诺氟沙星NMR谱

    化学绘图软件ChemDraw是一款在生化领域都可以使用的软件,诺氟沙星是一款常用的肠炎药,是生物化学领域的常见研究对象,在研究过程中需要预测它的NMR谱.这个时候如果用最新的ChemOffice 15 ...

  8. point-position2修改版

    说明: 在共面直线测试中,由于计算误差等原因,共面条件判断不准,但计算结果依然正确. // point-position2.cpp : 定义控制台应用程序的入口点. #include "st ...

  9. [转]C++ new操作符详解

    原文地址:http://blog.csdn.net/youdianmengxiangba/article/details/8233651 写在前面: 我最近写的一些博客都是因为在面试笔试过程中遇到的一 ...

  10. php读取xml的神器

    <?xml version="1.0 encoding="UTF-8"?> <humans> <zhangying> <name ...