学习基于springboot的java分布式中间件-消息中间件RabbitMQ(1)应用场景和Spring的事件驱动模型

概述

安装和介绍略

典型应用场景介绍

异步通信和服务解耦

以用户注册为实际场景,传统企业级应用处理用户注册的流程,首先是用户在界面上输入用户名、邮箱或者手机号等信息,确认无误之后,点击注册,前端会将这些信息提交到后端相关接口进行处理;后端在接收到这些信息后,会先对这些信息进行最基本的校验,校验成功之后会将信息写入数据库相关数据表,而为了用户注册信息的安全性,后端会调用邮件服务器提供的接口发送一封邮件用于验证用户账号的合法性,或者是调用短信服务的发送短信验证码接口对用户进行验证;验证通过后,才将响应信息返回给前端用户,并且提示注册成功。

但是如果在过程中,发送邮件或者短信业务逻辑出现了异常,整个流程将会终止——很显然这种处理方式对于当前互联网用户来说几乎不能接受。

仔细分析过后可以发现,核心的异物逻辑在于“判断用户注册信息的合法性并写入数据库”。而发送邮件或者短信服务在某种程度上并不属于用户注册的核心流程,故而可以将相应的服务从中解耦出来,并采用消息中间件RabbitMQ进行异步通信。
在这里插入图片描述

接口限流和消息分发

以商城用户抢购商品为例,在抢购商品活动开始的时候,由于商品数量有限,所有的用户几乎在同一时刻点击抢购按钮开始进行商品的抢购。
毫无疑问在抢购开始的那一刻,将会产生巨大的用户抢购请求,这些请求几乎在同一时间到达后端系统接口。

正常情况下,后端接口在接收到前端发过来的请求会执行如下流程:
首先会校验用户和商品信息的合法性,当校验通过之后,会判断当前商品的库存是否充足,如果充足,则代表当前用户将能成功抢购到商品,最后将用户抢购成功的相关数据记录存入数据库,并且异步通知用户抢购成功,尽快进行支付等等。

然而,仔细观察,后端系统在处理用户抢购的整体业务流程太长。而在这业务逻辑的处理过程中,存在着先取出库存,在进行判断,最后在进行减一的更新操作。在高并发请求的情况下,这些业务操作会给系统带来诸多的问题,比如商品超卖,数据不一致,用户等待时间长,系统接口挂掉等等问题,所以这单一的处理流程只适用于同一时刻前端请求很少的情况,而对于类似商城抢购,商品秒杀,等某一时刻产生高并发请求的情况,显得力不从心。

但是消息中间件的引入可以大大改善系统的整体业务流程和性能。
在这里插入图片描述
主要的系统优化处理有两个方面
(1)接口限流:前端产生的高并发请求不会像无头苍蝇一样立即到达后端系统接口,而是像明天上班时候的地铁限流一样,将这些请求按照先来后到的顺序存入RabbitMQ的队列,即在某种程度上实现接口限流。
(2)消息异步分发:当商品库存充足的时候,当前抢购的用户将可以抢到该商品,之后会异步地通过发短信,发邮件等方式通知用户抢购成功,并且告知用户尽快付款,即在某种程度上实现了消息异步分发

延迟处理

以12306抢票为例,但我们抢到火车票时候,12306会提醒用户请在30分钟内付款,正常情况用户会立刻点击付款,然后输入相应的支付密码支付火车票的费用,扣款成功后,12306会发送邮件或者短信通知用户抢票和付款成功等提示信息。

但是,实际情况下缺存在着一些特殊情况,比如用户抢到火车票后,由于各种原因而迟迟没有付款,过了30分钟后依旧没有支付车票的费用,导致系统自动取消了该笔订单。类似这种需要延迟一定时间后在处理的异物实际生产华你信中并不少见,传统企业应用处理这种业务的时候,是采用一个定时器去获取没有付款的订单,并且判断用户的下单时间距离当前的时间是否已经超过30分钟,如果是,则表示用户在30分钟内依旧没有付款,系统将自动失效该笔订单,且收回车票。

假如说是在春运抢票这样大数据,高并发的场景,某一时刻车票开抢后,正常情况下将会陆续有用户抢到车票,但距离车票付款成功是有一定的时间间隔的,在这段时间内,如果定时器频繁的从数据库中获取未付款状态的订单,数据量将大到难以想象,而且如果大批量的用户在30分钟内不付款,那从数据库中获取的数据量将一直增长,达到一定程度时候,将会给数据库服务器和应用服务器带来巨大的压力,甚至压垮服务器,导致抢票等业务全面崩溃,带来的后果不堪设想。

而消息中间件的引入,不管是从业务层面还是应用层面,都大大改善了系统的整体业务流程和性能。
在这里插入图片描述
从该优化流程中可以看出RabbitMQ的引入主要是取消了传统处理流程的定时器处理逻辑,取而代之的是采用RabbitMQ的延迟队列进行处理,延迟队列顾名思义,指的是可以延迟一定的时间在处理相应的业务逻辑。

基于Spring的事件驱动模型

在开始RabbitMQ的专有名词和各种消息模型之前,先了解一下传统的Spring应用系统中是如何构建消息模型,实现消息的异步分发和业务模块解耦的。了解一下内置的ApplicationEvent和ApplicationListener事件驱动模型。

Spring的事件驱动模型主要是由3大部分组成,包括发送消息的生产者,消息,和监听接收消息的消费者。这三者是绑定在一起的。可以说是形影不离,与RabbitMQ消息模型的生产者+消息+交换机+路由对应队列+消费者,十分的相似。

在这里插入图片描述

package com.zwx.middleware.event;

import lombok.Data;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;

import java.io.Serializable;

/**
 * 用户登录成功后的事件实体
 **/

@ToString
@Data
public class LoginEvent extends ApplicationEvent implements Serializable{

    private String userName; //用户名
    private String loginTime; //登录时间
    private String ip; //所在ip

    public LoginEvent(Object source) {
        super(source);
    }

    //继承ApplicationEvent类时需要重载的构造方法
    public LoginEvent(Object source, String userName, String loginTime, String ip) {
        super(source);
        this.userName = userName;
        this.loginTime = loginTime;
        this.ip = ip;
    }
}
package com.zwx.middleware.event;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;


@Component
@EnableAsync//允许异步执行
public class Consumer implements ApplicationListener<LoginEvent>{

    private static final Logger log= LoggerFactory.getLogger(Consumer.class);

    /**
     * 监听消费消息-事件信息
     * @param loginEvent
     */
    @Override
    @Async
    public void onApplicationEvent(LoginEvent loginEvent) {
        log.info("Spring事件驱动模型-接收消息:{}",loginEvent);

        //TODO:后续为实现自身的业务需求-比如写入数据库等等

    }
}

package com.zwx.middleware.event;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * spring的事件驱动模型-生产者
 **/
@Component
@Slf4j
public class Publisher {


    @Autowired
    private ApplicationEventPublisher publisher;

    public void sendMsg() throws Exception{
        LoginEvent event=new LoginEvent(this,"debug",new SimpleDateFormat("yyyyy-MM-dd HH:mm:ss").format(new Date()),"127.0.0.1");
        publisher.publishEvent(event);
        log.info("Spring事件驱动模型-发送消息:{}",event);
    }

}
package com.zwx.middleware;

import com.zwx.middleware.event.Publisher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class EventTest {

    @Autowired
    private Publisher publisher;

    @Test
    public void test1() throws Exception{
        publisher.sendMsg();
    }

}

在这里插入图片描述
由于我们采用异步的方式接受消息,所以控制台输出结果中,可以看到接受处理消息的方式采用不同于主线程main的子线程去执行,这种方式可以达到异步通信和业务模块解耦的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值