阿里云消息队列


喜欢这句话,人与人的相遇,正是如此偶然,又放佛命中注定。

我们或多或少都有一些自己的喜欢的东西,文章,篮球,跑步,有些注定要坚持,用一生的时间来寻找,梦想这件事,注定是一辈子的事情,要用毕生的心血去实现它,一般写文章前,我总会闲谈几句,抒发一下自身情怀的东西,今天想给大家分享一下看到的一篇文章,关于雷军的,听不错的,如果你有时间驻足停留下,静静的读一下,也许对于有帮助,地址放在这里

雷军50岁身价破1000亿:决定人生胜负的,是这5条规律

https://mp.weixin.qq.com/s/XXQe0bnanrOSN2_if22zOA

如果你读完了,或者对于这心灵鸡汤没感觉,还是脚踏实地的走好每一步,接下来,我们要探讨的就是阿里云的消息队列


或许你看到了,阿里的产品,设计到各方面,这一点对于程序员的我们是一种福利,但是产品的背后是辛勤的努力,加班加点的工作,也正好应住雷军说的话:

努力工作,克制贪婪

是世界上最笨也最高明的办法

不得不说,我做的还不够多。

回归正题,那阿里云消息队列如何做的呢?为什么要用它?产生的效果如何?

阿里云消息队列用的是rocketmq,基于apache下的开源软件,看来大家都喜欢开源,而Kafka还在自测中,所以她们内部都使用的是这些,何况我们呢?对于一般中小企业的项目而言,使用现成的开源第三方软件成为我们快速制胜的必备把握,毕竟自己搭建费时费力,或许还有可能出现不稳定的因素,多一种选择,就多一条路。

消息队列,无非就是和数据结构相吻合,还记得那个是先进先出,那个是先进后出,排队等待。

那让我们看看消息队列的用途:

  1. 解耦在项目开始时,很难预测项目的未来需求。通过在进程之间引入层,消息队列创建一个隐式的基于数据的接口,两个进程都实现。这允许您通过简单地确保它们遵守相同的接口要求来独立地扩展和修改这些进程。
  2. 冗余有时处理数据时进程失败。除非数据持续存在,否则它将永远丢失。队列通过持久化数据直到完全处理来缓解这一点。许多消息队列使用的put-get-delete范例需要一个进程来明确指出在邮件从队列中删除之前已经完成了处理消息,确保您的数据保持安全,直到完成。
  3. 可扩展性因为消息队列使您的进程脱钩,因此可以轻松地扩展将消息添加到队列或处理的速率; 只需添加另一个进程。没有代码需要更改,不需要调整任何配置。缩放比添加更多的权力一样简单。
  4. 弹性和刺激性当您的应用程序打到黑客新闻的首页时,您将看到不寻常的流量。您的应用程序需要能够继续运行这种增加的负载,但流量是异常的,而不是标准; 在待机状态下拥有足够的资源来处理这些尖峰是浪费的。消息队列将允许被困扰的组件通过增加的负载进行斗争,而不是通过请求过载,并且完全失败。查看我们的Spikability博客文章了解更多信息。
  5. 弹性当您的架构部分发生故障时,不需要将整个系统关闭。消息队列解耦过程,因此如果正在处理来自队列的消息的进程失败,那么仍然可以将消息添加到队列中以在系统恢复时进行处理。接受稍后重试或处理的请求的这种能力通常是不方便的客户和沮丧的客户之间的差异。
  6. 交付保证
    消息队列提供的冗余保证消息将被最终处理,只要进程正在读取队列。除此之外,IronMQ还提供了一次唯一的保证。无论从队列中提取数据有多少进程,每个消息只能一次处理。这是可能的,因为检索消息“保留”该消息,暂时将其从队列中删除。除非客户明确声明已完成该消息,否则该消息将被放置在队列中,以在可配置的时间量之后进行处理。
  7. 订购保证
    在很多情况下,处理数据的顺序很重要。消息队列是固有排序的,能够提供数据将以特定顺序处理的保证。IronMQ保证消息将使用FIFO(先进先出)处理,因此消息放在队列上的顺序是从中检索消息的顺序。
  8. 缓冲在任何不平凡的系统中,都将需要不同处理时间的组件。例如,上传图像所需的时间要比应用过滤器的时间要少。消息队列通过提供缓冲层来帮助这些任务以最高效率运行,而写入队列的过程可以尽可能快地编写,而不是受到从队列读取进程的准备的约束。该缓冲区有助于控制和优化数据流经您系统的速度。
  9. 了解数据流
    在分布式系统中,掌握用户操作完成时间的长短,为什么是一个巨大的问题。消息队列通过处理速率有助于轻松识别不良数据流或数据流不最优的区域。
  10. 异步通信
    很多次,您不想或需要立即处理消息。消息队列启用异步处理,允许您将消息放在队列上,而不是立即处理。您可以随意添加多个消息,然后随意处理。

以上几条,我们或多或少都会用到几处

针对以上,在我们项目中我使用了 解耦,订购,异步通信

解耦:就是把耦合度高的模块或者接口拆分

订购:订单购买流程要保证订单与库存之间的衔接关系,不会因为库存系统崩掉而影响下单流程

异步通信:如果对于不要求时间必须在某一点必须到达,例如,短信的发送

具体举例如下:

项目应用:****

技术栈:spring boot + spring data jpa +spring security+ redis + 消息队列等等

#阿里云配置
ali:
  accessKeySecret: *********
  accessKeyID: ********
  regionId: cn-beijing
  mq:
    host: http://onsaddr-internet.aliyun.com/rocketmq/nsaddr4client-internet
    producerId: 以PID开始
    consumerId: 以CID开始
    topic:topic
    tag: TagA
    sendMsgTimeoutMillis: 3000
    suspendTimeMillis: 100
    maxReconsumeTimes: 20

阿里云服务:

package com.***.service;
import com.aliyun.openservices.ons.api.*;
import com.aliyun.openservices.ons.api.bean.ConsumerBean;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.aliyun.openservices.ons.api.bean.Subscription;
import com.***.listener.AliMQConsumerListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class AliMQServiceImpl {


    @Value("${ali.accessKeyID}")
    private String accessKeyId;

    @Value("${ali.accessKeySecret}")
    private String accessKeySecret;

    @Value("${ali.mq.host}")
    private String onsHost;

    @Value("${ali.mq.producerId}")
    private String producerId;

    @Value("${ali.mq.consumerId}")
    private String consumerId;

    @Value("${ali.mq.topic}")
    private String topic;

    @Value("${ali.mq.tag}")
    public String tag;

    //超时时间
    @Value("${ali.mq.sendMsgTimeoutMillis}")
    public String sendMsgTimeoutMillis;

    @Value("${ali.mq.suspendTimeMillis}")
    public String suspendTimeMillis;

    @Value("${ali.mq.maxReconsumeTimes}")
    public String maxReconsumeTimes;

    private static final Logger logger = LoggerFactory.getLogger(AliMQServiceImpl.class);

    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public ProducerBean getProducer() {
        ProducerBean producerBean = new ProducerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.ProducerId, producerId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKeyId);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, accessKeySecret);
        properties.put(PropertyKeyConst.SendMsgTimeoutMillis, sendMsgTimeoutMillis);
        properties.put(PropertyKeyConst.ONSAddr, onsHost);
        producerBean.setProperties(properties);
        return producerBean;
    }

    //此处是注入Bean,这样才能起到作用
    @Bean
    MessageListener aliMessageListener(){
        return new AliMQConsumerListener();
    }


    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public ConsumerBean getConsumer() {
        ConsumerBean consumerBean = new ConsumerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.ConsumerId, consumerId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKeyId);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, accessKeySecret);
        properties.put(PropertyKeyConst.SuspendTimeMillis, suspendTimeMillis);
        properties.put(PropertyKeyConst.MaxReconsumeTimes, maxReconsumeTimes);
        properties.put(PropertyKeyConst.ONSAddr, onsHost);
        consumerBean.setProperties(properties);
        Subscription subscription = new Subscription();
        subscription.setTopic(topic);
        subscription.setExpression(tag);
        Map<Subscription, MessageListener> map = new HashMap();
        map.put(subscription,aliMessageListener());
        consumerBean.setSubscriptionTable(map);
        consumerBean.start();
        return consumerBean;
    }

    // 异步发送消息
    public void sendMessage(String topic, String tag, byte[] body) {
        //调用MQConfig配置文件,此处统一处理
        Producer producer = this.getProducer();
        Message msg = new Message(topic, tag, body);
        // 异步发送消息, 发送结果通过 callback 返回给客户端。
        producer.sendAsync(msg, new SendCallback() {
            @Override
            public void onSuccess(final SendResult sendResult) {
                // 消费发送成功
                logger.info("send message success. topic=" + sendResult.getTopic() + ", msgId=" + sendResult.getMessageId());
            }
            @Override
            public void onException(OnExceptionContext context) {
                // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理
                logger.info("send message failed. topic=" + context.getTopic() + ", msgId=" + context.getMessageId());
            }
        });
        // 在 callback 返回之前即可取得 msgId。
        logger.info("send message async. topic=" + msg.getTopic() + ", msgId=" + msg.getMsgID());
    }

    public String getAccessKeyId() {
        return accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
        this.accessKeyId = accessKeyId;
    }

    public String getAccessKeySecret() {
        return accessKeySecret;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        this.accessKeySecret = accessKeySecret;
    }

    public String getOnsHost() {
        return onsHost;
    }

    public void setOnsHost(String onsHost) {
        this.onsHost = onsHost;
    }

    public String getProducerId() {
        return producerId;
    }

    public void setProducerId(String producerId) {
        this.producerId = producerId;
    }

    public String getConsumerId() {
        return consumerId;
    }

    public void setConsumerId(String consumerId) {
        this.consumerId = consumerId;
    }

    public String getTopic() {
        return topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public String getSendMsgTimeoutMillis() {
        return sendMsgTimeoutMillis;
    }

    public void setSendMsgTimeoutMillis(String sendMsgTimeoutMillis) {
        this.sendMsgTimeoutMillis = sendMsgTimeoutMillis;
    }

    public String getSuspendTimeMillis() {
        return suspendTimeMillis;
    }

    public void setSuspendTimeMillis(String suspendTimeMillis) {
        this.suspendTimeMillis = suspendTimeMillis;
    }

    public String getMaxReconsumeTimes() {
        return maxReconsumeTimes;
    }

    public void setMaxReconsumeTimes(String maxReconsumeTimes) {
        this.maxReconsumeTimes = maxReconsumeTimes;
    }

}

package com.***.listener;

import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.***.domain.request.RequestNotify;
import com.***.service.CommonService;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


public class AliMQConsumerListener implements MessageListener {

    @Autowired
    private CommonService commonService;

    private static final Logger logger = LoggerFactory.getLogger(AliMQConsumerListener.class);

    public Action consume(Message message, ConsumeContext context) {
        String msg = "";
        try {
            //TODO 消息队列处理订单业务逻辑
            msg = new String(message.getBody(), "UTF-8");
            logger.info("订阅消息:" + msg);
            JSONObject obj = JSONObject.fromObject(msg);
            RequestNotify requestNotify = (RequestNotify) JSONObject.toBean(obj, RequestNotify.class);
            //消费代码 核心代码要处理等事情
            boolean isTrue = commonService.pseudocode(requestNotify);
            if (isTrue) {
                return Action.CommitMessage;
            }
            return Action.ReconsumeLater;
        } catch (Exception e) {
            //消费失败
            logger.error("消费失败:" + msg);
            return Action.ReconsumeLater;
        }
    }

}
   这样项目在启动的时候,就可以监听了,可以test一下发送到消息队列里边-然后自动会监听
AliMQConsumerListener类,

  之前错误的原因:就是监听不到,是因为bean注入的时候,有先后顺序,启动spring boot项目先加载了其他bean对象,优先级的先后关系,具体在这里都不讲了,今天就先到这里吧,天色已晚,已有睡意,送君一首歌:Zombie

https://www.xiami.com/play?ids=/song/playlist/id/2080673/object_name/default/object_id/0#loaded


外文翻译:  https://blog.iron.io/top-10-uses-for-message-queue/






猜你喜欢

转载自blog.csdn.net/chajinglong/article/details/80581643