阿里云mns消息服务(研究主题模式)

阿里云mns消息服务(研究主题模式)

  • 简介
    • 模式、特点、区别
    • 队列
      • 简介
      • 模型示意图
      • 详情
      • 模型示意图
      • 队列的特性
      • 主题操作
        • 创建队列
        • 发送消息
          • 接收消息
    • 主题
      • 简介
      • 模型示意图
      • 数据传播示意图
      • 主题操作
          • 引入依赖
          • 创建主题
          • 创建订阅
          • 发送消息
          • 删除主题
            • 附加
    • 区别

简介

阿里云消息服务(Message Service)是一种高效、可靠、安全、便捷、可弹性扩展的分布式消息服务。MNS能够帮助应用开发者在他们应用的分布式组件上自由的传递数据、通知消息,构建松耦合系统。
消息服务同时支持各种类型消息推送,其中和短信前后端的无缝整合更高效的为用户提供了大批量短信发送能力。

模式、特点、区别

消息服务提供了两种模型:

  • 队列模型
  • 主题模型
    两种功能模型的区别:
  • 队列模型支持一对一发送和接收消息;
  • 主题模型支持一对多发布和订阅消息,并且支持多种消息推送方式。

队列

简介

队列模型旨在提供高可靠高并发的一对一消费模型。即队列中的每一条消息都只能够被某一个消费者进行消费

模型示意图

阿里云mns消息服务(研究主题模式)_第1张图片

详情

消息进入队列的时候不一定是按照发送消息的顺序存储,有可能会出现消息位置倒置,所以若要遵循先进先出,则可以加个序列之类,然后根据序号进行业务处理

模型示意图

阿里云mns消息服务(研究主题模式)_第2张图片

队列的特性

不可重复消费
队列接口适用于点对点的消息收发,当接收消息时,需要应用端自行轮询获取消息(拉模式)。
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息,消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

主题操作

创建队列

    /**
     * 创建队列
     */
public void createQueue() {
        CloudAccount account = new CloudAccount("YourAccessId", "YourAccessKey", "MNSEndpoint");
        MNSClient client = account.getMNSClient(); // 在程序中,CloudAccount以及MNSClient单例实现即可,多线程安全
        String queueName = "TestQueue";
        QueueMeta meta = new QueueMeta(); //生成本地QueueMeta属性,有关队列属性详细介绍见https://help.aliyun.com/document_detail/27476.html
        meta.setQueueName(queueName);  // 设置队列名
        meta.setPollingWaitSeconds(15);
        meta.setMaxMessageSize(2048L);
        try {
            CloudQueue queue = client.createQueue(meta);
        } catch (ClientException ce)
        {
            System.out.println("Something wrong with the network connection between client and MNS service."
                    + "Please check your network and DNS availablity.");
            ce.printStackTrace();
        } catch (ServiceException se)
        {
            se.printStackTrace();
            logger.error("MNS exception requestId:" + se.getRequestId(), se);
            if (se.getErrorCode() != null) {
                if (se.getErrorCode().equals("QueueNotExist"))
                {
                    System.out.println("Queue is not exist.Please create before use");
                } else if (se.getErrorCode().equals("TimeExpired"))
                {
                    System.out.println("The request is time expired. Please check your local machine timeclock");
                }
            /*
            you can get more MNS service error code from following link:
            https://help.aliyun.com/document_detail/mns/api_reference/error_code/error_code.html
            */
            }
        } catch (Exception e)
        {
            System.out.println("Unknown exception happened!");
            e.printStackTrace();
        }
        client.close();  // 程序退出时,需主动调用client的close方法进行资源释放
    }

发送消息

/**
     * 发送消息
     */
    public void sendMessage() {
        CloudAccount account = new CloudAccount("YourAccessId", "YourAccessKey", "MNSEndpoint");
        MNSClient client = account.getMNSClient(); // 在程序中,CloudAccount以及MNSClient单例实现即可,多线程安全
        try {
            CloudQueue queue = client.getQueueRef("TestQueue");
            Message message = new Message();
            message.setMessageBody("I am test message ");
            Message putMsg = queue.putMessage(message);
            System.out.println("Send message id is: " + putMsg.getMessageId());
        } catch (ClientException ce)
        {
            System.out.println("Something wrong with the network connection between client and MNS service."
                    + "Please check your network and DNS availablity.");
            ce.printStackTrace();
        } catch (ServiceException se)
        {
            se.printStackTrace();
            logger.error("MNS exception requestId:" + se.getRequestId(), se);
            if (se.getErrorCode() != null) {
                if (se.getErrorCode().equals("QueueNotExist"))
                {
                    System.out.println("Queue is not exist.Please create before use");
                } else if (se.getErrorCode().equals("TimeExpired"))
                {
                    System.out.println("The request is time expired. Please check your local machine timeclock");
                }
            /*
            you can get more MNS service error code from following link:
            https://help.aliyun.com/document_detail/mns/api_reference/error_code/error_code.html
            */
            }
        } catch (Exception e)
        {
            System.out.println("Unknown exception happened!");
            e.printStackTrace();
        }
        client.close();  // 程序退出时,需主动调用client的close方法进行资源释放
    }
接收消息
    /**
     * 接收并删除消息
     */
    public void getAndDelMessage() {
        CloudAccount account = new CloudAccount("YourAccessId", "YourAccessKey", "MNSEndpoint");
        MNSClient client = account.getMNSClient(); // 在程序中,CloudAccount以及MNSClient单例实现即可,多线程安全
        try{
            CloudQueue queue = client.getQueueRef("TestQueue");
            Message popMsg = queue.popMessage();
            if (popMsg != null){
                System.out.println("message handle: " + popMsg.getReceiptHandle());
                System.out.println("message body: " + popMsg.getMessageBodyAsString());
                System.out.println("message id: " + popMsg.getMessageId());
                System.out.println("message dequeue count:" + popMsg.getDequeueCount());
                //删除已经取出消费的消息
                queue.deleteMessage(popMsg.getReceiptHandle());
                System.out.println("delete message successfully.\n");
            }
            else{
                System.out.println("message not exist in TestQueue.\n");
            }
        } catch (ClientException ce)
        {
            System.out.println("Something wrong with the network connection between client and MNS service."
                    + "Please check your network and DNS availablity.");
            ce.printStackTrace();
        } catch (ServiceException se)
        {
            se.printStackTrace();
            logger.error("MNS exception requestId:" + se.getRequestId(), se);
            if (se.getErrorCode() != null) {
                if (se.getErrorCode().equals("QueueNotExist"))
                {
                    System.out.println("Queue is not exist.Please create before use");
                } else if (se.getErrorCode().equals("TimeExpired"))
                {
                    System.out.println("The request is time expired. Please check your local machine timeclock");
                }
            /*
            you can get more MNS service error code from following link:
            https://help.aliyun.com/document_detail/mns/api_reference/error_code/error_code.html
            */
            }
        } catch (Exception e)
        {
            System.out.println("Unknown exception happened!");
            e.printStackTrace();
        }
        client.close();
    }

主题

简介

主题订阅模型旨在提供一对多的发布订阅以及消息通知功能,支持用户实现一站式多种消息通知方式:

  • 推送到用户指定 HttpServer
  • 推送到用户指定的 Queue(用户可以从该 Queue 拉取消息)
  • 推送到邮件(组)
  • 推送到短信(列表)
  • 移动推送(计划支持)

模型示意图

阿里云mns消息服务(研究主题模式)_第3张图片

数据传播示意图

阿里云mns消息服务(研究主题模式)_第4张图片
说明:

  • 图中所示,Topic上有多个订阅,分别使用了不同的推送方式,有推送到 HttpServer 的,有推送到 Queue 的,有推送到邮箱中的等等。
  • 图中所示,一条消息发布到 Topic 中后,会被分别推送到不同的订阅指定的 Endpoint 中。
  • 图中所示,订阅支持消息过滤,可以在订阅中指定过滤标签。对于没有指定过滤标签的订阅,发送消息时,无论有没有指定消息标签,消息都可以推送到指定的接收端;对于指定了过滤标签的订阅(Subscription2),发送的消息,只有指定了匹配的消息标签(红色的消息B),消息才会推送到指定的接收端。
    ###主题消息特性
  • 支持通知消息
  • 支持一对多广播消息
  • 支持消息标签过滤
  • 支持多种投递方式
  • 消息投递保障
  • 支持云产品事件通知
  • 支持日志管理
  • 支持云监控

主题操作

    private static final String endPoint = "********************";
    private static final String accessId = "*********************";
    private static final String accessKey = "*************************";
引入依赖
        <dependency>
            <groupId>com.aliyun.mnsgroupId>
            <artifactId>aliyun-sdk-mnsartifactId>
            <version>1.1.8version>
        dependency>
创建主题
    /**
     * 创建topic
     */
    private void createTopic() {
        CloudAccount account = new CloudAccount(accessId, accessKey, endPoint);
        MNSClient client = account.getMNSClient(); // 在程序中,CloudAccount以及MNSClient单例实现即可,多线程安全
        String topicName = "TestTopic";
        TopicMeta meta = new TopicMeta();
        meta.setTopicName(topicName);
        try {
            CloudTopic topic = client.createTopic(meta);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("create topic error, " + e.getMessage());
        }
        client.close();// 程序退出时,需主动调用client的close 方法进行资源释放
    }
创建订阅
/**
     * 订阅topic
     */
    public void subscribeTopic(){
        try {
            QueueMeta queueMeta = new QueueMeta();
            queueMeta.setQueueName("TestSubForQueue");
            this.client.createQueue(queueMeta);
            CloudTopic topic = this.client.getTopicRef("TestTopic");
            SubscriptionMeta subMeta = new SubscriptionMeta();
            subMeta.setSubscriptionName("TestSub");
            subMeta.setNotifyContentFormat(format);
            subMeta.setEndpoint(topic.generateQueueEndpoint("TestSubForQueue"));
            topic.subscribe(subMeta);
        } catch (Exception e){
            log.error("subscribe Topic error ,error:{}", e.getMessage());
            e.printStackTrace();
        }
        this.client.close();
    }
发送消息
/**
     * 发送消息
     */
    private void PublishMessage() {
        CloudTopic topic = this.client.getTopicRef("TestTopic");
        try {
            //可以使用TopicMessage结构,选择不进行Base64加密
            TopicMessage msg = new RawTopicMessage();
            msg.setMessageBody("Test Message Body: Hello World");
            //设置该条发布消息的filterTag
            if (StringUtils.isNotBlank(messageTag))
            {
                msg.setMessageTag("Hello"");
            }
            msg = topic.publishMessage(msg);
            String messageId = msg.getMessageId();
            String requestId = msg.getRequestId();
            log.info("请求消息messageId:{}", messageId);
            log.info("请求消息requestId:{}", requestId);
        } catch (Exception e) {
            log.error("send message error,{}", e.getMessage());
            e.printStackTrace();
        }
        this.client.close();
    }
删除主题
/**
     * 删除主题
     */
    private void deleteTopic(){
        CloudTopic topic = client.getTopicRef("TestTopic");
        try {
            topic.delete();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("delete topic error,{}", e.getMessage());
        }
        client.close();
    }
附加

主题还有个方式也可以实现一对多

public CloudPullTopic createPullTopic(TopicMeta topicMeta, Vector<String> queueNameList, boolean needCreateQueue, QueueMeta queueMetaTemplate)
public CloudPullTopic createPullTopic(TopicMeta topicMeta, Vector<String> queueNameList)

其中,TopicMeta 是创建topic的meta 设置, queueNameList里指定topic消息推送的队列名列表;needCreateQueue表明queueNameList是否需要创建;queueMetaTemplate是创建queue需要的queue meta 参数设置
大致意思是:直接将queue集合关联到某个topic ,也可以选择是否创建queue,默认是false不创建,个人感觉有个缺点是,我要一开始确定好我有几个queue来关联,若后面想增加一个queue就不太好做。
相对上面那种方式,可以后续加订阅关联。

区别

  • 队列适合点对点模式,生产者发送一条消息到queue,一个queue可以有很多消费者,但是一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有 一个可用的消费者,所以Queue实现了一个可靠的负载均衡。
  • 主题适合一对多模式,一条通知消息可以同时被多个订阅者接收和消费。主题模型支持服务端主动将消息推送给用户指定的回调地址(Endpoint),消除用户程序不必要的轮询和资源消耗。

你可能感兴趣的:(阿里云mns消息服务(研究主题模式))