四、spring cloud alibaba学习(RocketMQ)

拜拜、爱过 提交于 2020-02-08 01:09:24

 

假如有一个文章审核,通过后加积分的需求,审核是这个方法的主体,而加积分并不需要实时同步,这时,我们就可以将加积分的功能变为异步执行。

Spring实现异步的方法

1.AsyncRestTemplate

https://blog.csdn.net/jiangchao858/article/details/86709750

2.@Async注解

https://spring.io/guides/gs/async-method/

3.WebClient(Spring 5.0引入)

https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web-reactive.html#webflux-client

4.MQ

引入MQ后,生产者生产消息,然后将消息发送给MQ,消费者监听这个消息所属的topic,一旦接收到消息,就进行相关的处理。

MQ的适用场景

1.异步处理:不解释

2.流量削峰填谷:例如秒杀活动,可以用MQ控制参加人数,人数一旦达到阈值,就丢弃请求或跳转到错误页。防止应用被流量洪峰打死。

3.解耦微服务:假如A调用B,B挂了,虽然有sentinel可以保护A不被B拖死,但是依然无法正常返回,用MQ以后,A把消息发给MQ,就算B挂了也没事,后面B恢复正常会从MQ中拿消息处理,AB也解耦了。

RocketMQ

https://git.imooc.com/coding-358/rocketmq-dev-guide

搭建RocketMQ

1.http://rocketmq.apache.org/release_notes/release-notes-4.5.1/下载Binary并解压

2.配置环境变量:变量名:ROCKETMQ_HOME, 变量值:MQ解压路径\MQ文件夹名

3.启动NAMESERVER:cmd到bin目录下,执行start mqnamesrv.cmd

4.启动BROKER:cmd到bin目录下,执行start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

tips:假如弹出提示框提示‘错误: 找不到或无法加载主类 xxxxxx’。打开runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号。保存并重新执行start语句。

RocketMQ插件部署

1.下载https://github.com/apache/rocketmq-externals.git

2.配置:进入rocketmq-console\src\main\resources文件夹,打开application.properties进行配置。

3.修改依赖:把pom.xml中<rocketmq.version>4.4.0</rocketmq.version>改成你的RocketMQ版本

4.改代码:修改pom以后org.apache.rocketmq.console.service.impl.MessageServiceImpl#queryMessageByTopic编译会报错,DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, null);

改为

RPCHook rpcHook = null;

DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);

5.编译启动:进入rocketmq-externals\rocketmq-console文件夹,执行mvn clean package -DskipTests

6.cmd进入target文件夹,执行java -jar rocketmq-console-ng-1.0.0.jar

7.浏览器中输入127.0.0.1:配置端口(例如17890),成功后即可查看。

生产可用的集群搭建

http://www.itmuch.com/books/rocketmq/operation.html

RocketMQ的术语和概念

主题Topic:

    一类消息的集合,RocketMQ的基本订阅单位

        例如:听广播需要知道是什么调频,fm89.7之类的,这个调频就相当于topic

消息模型:

    Producer(生产者,生产消息)

    Broker(消息代理,存储消息、转发消息)

    Consumer(消费者,消费消息)

部署结构

    NameServer(名字服务)生产者/消费者通过名字服务查找各主题相应的Broker IP列表

        相当于RocketMQ的服务发现组件

    BrokerServer(代理服务器)消息中转角色,负责存储消息、转发消息

消费模式

    PullConsumer(拉取式消费)应用调用Consumer的拉取信息方法从BrokerServer拉取消息

    PushConsumer(推动式消费)Broker收到消息后主动推送给消费端,该模式实时性较高

组Group

    ProducerGroup(生产者组)同一类Producer的集合,这类Producer发送同一类消息且

    ConsumerGroup(消费者组)同一类Consumer的集合,这类Consumer通常消费同一类消息

消息传播模式

    Clustering(集群)相同ConsumerGroup的每个Consumer实例平均分摊消息

    Broadcasting(广播)相同ConsumerGroup的每个Consumer实例都接受全量的消息

消息类型

    普通消息、顺序消息、定时/延时消息、事务消息

编写生产者

加依赖

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.3</version>
</dependency>

这里要自己写一下版本,因为使用的RocketMQ版本不一样

写配置

rocketmq:
  name-server: 127.0.0.1:9876
  producer:
    #必须指定group,否则启动会报无法初始化RocketMQ
    group: test-group

写代码

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserAddBonusMsgDTO{
    //为谁加积分
    private Integer userId;
    //加多少积分
    private Integer bonus;
}
public class ShareService{
    private final RocketMQTemplate rocketMQTemplate;
    public ShareDTO findById(Integer id){
        this.rocketMQTemplate.convertAndSend(
            "add-bonus",
            UserAddBonusMsgDTO.builder()
                .userId(share.getUserId())
                .bonus(50)
                .build()
        );
    }

发送请求后,在控制台就能看到一个叫add-bonus的主题了

编写消费者

加依赖

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.3</version>
</dependency>

写配置

rocketmq:
  name-server: 127.0.0.1:9876

 写业务代码

@Service
@RocketMQMessageListener(consumerGroup = "consumer-group" , topic = "add-bonus")
public class AddBonusListener implements RocketMQListener<UserAddBonusMsgDTO>{
    @Override
    public void onMessage(UserAddBonusMsgDTO message){
        //当收到消息的时候执行的业务
        ...
    }
}

启动以后就可以自动运行

RocketMQ的分布式事务

一般添加注解@Transactional(rollbackFor = Exception.class),发生异常,数据库就会回滚,但是由于有了MQ,发生异常后,数据库回滚了,但是MQ消息已经发出去了,就没办法取消了,基于这种情况,RocketMQ提供了事务消息。

流程:

概念术语:

消息的三种状态:

实现分布式事务

public class ShareService{
    private final RocketMQTemplate rocketMQTemplate;
    public ShareDTO findById(Integer id){
        String transactionId = UUID.randomUUID().toString();
        //发送半消息
        this.rocketMQTemplate.sendMessageInTransaction(
            "tx-add-bonus-group",                //随便起个名称
            "add-bonus",                         //topic
            MessageBuilder.withPayload(          //消息
                UserAddBonusMsgDTO.builder()
                    .userId(share.getUserId())
                    .bonus(50)
                    .build()
                .setHeader(RocketMQHeaders.TRANSACTION_ID , transactionId)
                .setHeader("share_id" , id)
            ).build(),
            auditDTO                             //arg
        );
    }

 

@RocketMQTransactionListener(txProducerGroup = "tx-add-bonus-group")
public class AddBonusTransactionListener implements RocketMQLocalTransactionListener{
    //本地事务    
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg , Object arg){
        MessageHeaders headers = msg.getHeaders();
        String transactionId = (String)headers.get(RocketMQHeaders.TRANSACTION_ID);
        Integer shareId = Integer.valueOf((String)header.get("share_id"));
        //接下来就可以执行本地事务了
        try{
            ...
            //建一个日志表,正常通过后,将日志记录写到表里,保证如果要回查可以查到
            //如果本地事务正常通过
            return RocketMQLocalTransactionState.COMMIT;
        }catch (Exception e){
            return RocketMQLocalTransactionState.ROLLBACK;
        }
        
    }
    //回查
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg){
        MessageHeaders headers = msg.getHeaders();
        String transactionId = (String)headers.get(RocketMQHeaders.TRANSACTION_ID);
        //回查 如果有就返回commit,不是空就rollback
        if(成功){
            return RocketMQLocalTransactionState.COMMIT;
        }else{
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }
}

Spring Cloud Stream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!