Redis实现交易数据实时监控系统实战

该博客介绍了一个利用Redis、WebSocket构建的实时交易数据监控系统,详细讲解了系统设计,包括交易服务、监控服务和监控应用。系统通过Redis的消息发布与订阅功能,以及其数据结构特性进行数据统计,并通过WebSocket将实时数据推送给前端。
摘要由CSDN通过智能技术生成

文章分为四个部分

  • 1、主要功能
  • 2、运用的技术
  • 3、系统设计
  • 4、优化与总结
1、主要功能

对平台支付网关的交易订单进行实时的统计,包括实时的交易金额与交易订单量、不同支付方式的交易总额、订单量以及占比、当天各个时间段的数据统计折线图,实现效果图如下:

untitled.jpg

2、运用的技术
  • Redis:利用Redis的消息发布与订阅功能、以及List、SortedSet、Hash的数据结构特性
  • WebSocket:负责将实时汇总的交易数据推送至浏览器客户端
3、系统设计

实时交易数据监控系统所涉及的工程包括交易服务、监控统计服务、监控应用(Dubbo服务化)。

  • 交易服务在交易成功后向Redis中发布消息并将数据发送至Redis的list队列
  • 监控服务负责Redis消息的订阅并进行统计,统计完成后将实时的统计结果再次发送至Redis
  • 监控应用作为WebSocket的服务端,也负责监听监控服务推送过来的实时统计数据并通过WebSocket将数据推送至客户端。

Redis数据结构图如下: Redis数据结构.png

利用list的lpush、lpop功能进行对数据的存取操作,SortedSet最开始主要是用于排序,将交易时间作为score进行排序,但是因为涉及到一些数据的计算,在高并发以及分布式部署的情况下,利用SortedSet进行数据统计是会存在问题的,文末会提到,hash结构主要是用于对数据进行原子性的计算。

UML时序图如下: Paste_Image.png

3.1、交易系统——支付服务

支付服务在交易成功后,会给Redis发布一条订单记录消息,并向Redis的list列表lpush一条同样的订单记录信息,为了不影响正常的支付业务流程,所以采用的是异步的方式,伪代码如下:

/**
	 * Redis消息通道
	 */
	@Value("#{settings['redis.trade.channel']}")
	private String redisChannel;
	/**
	 * 微信支付的订单队列key
	 */
	@Value("#{settings['redis.trade.wxDetails']}")
	private String redisWxQueue;
	/**
	 * 支付宝支付的订单队列key
	 */
	@Value("#{settings['redis.trade.alipayDetails']}")
	private String redisAlipayQueue;
	/**
	 * 单个线程的线程池
	 */
	protected static ExecutorService executorService = Executors.newSingleThreadExecutor();
	
	/**
	 * 交易成功后需要执行的业务逻辑
	 * @param paymentRecord
	 */
	public void successPayment(final PaymentRecord paymentRecord) {
		// do otherthing...
		// 异步发送消息
		executorService.submit(new Runnable() {
			@Override
			public void run() {
				try {
					pushPaymentRecordMonitorVo(paymentRecord);
				} catch (Exception e) {
					log.error("payment send to redis fail,PaymengRecord:" + JsonUtil.toJsonString(paymentRecord));
				}
			}
		});
	}
	/**
	 * 将交易成功的订单信息插入至Redis队列并发送一条通知通知
	 * @param paymentRecord
	 * @throws Exception
	 */
	private void pushPaymentRecordMonitorVo (PaymentRecord paymentRecord) throws Exception{
		PaymentRecordMonitorVo paymentRecordMonitorVo = new PaymentRecordMonitorVo();
		paymentRecordMonitorVo.setMerchantOrderNo(paymentRecord.getMerchantOrderNo());
		paymentRecordMonitorVo.setPayWay(paymentRecord.getPayWayCode() == null ? null : paymentRecord.getPayWayCode().name());
		paymentRecordMonitorVo.setTradeTime(paymentRecord.getPaySuccessTime());
		paymentRecordMonitorVo.setAmount(paymentRecord.getOrderAmount());
		log.info("订单消息插入Redis队列...");
		if (paymentRecord.getPayWayCode() != null && paymentRecord.getPayWayCode().equals(PayWayEnum.WEIXIN)) {
			JedisHelper.dataCluster().lpush(redisWxQueue,JsonUtil.toJsonString(paymentRecordMonitorVo));
		} else if (paymentRecord.getPayWayCode() != null && paymentRecord.getPayWayCode().equals(PayWayEnum.ALIPAY)) {
			JedisHelper.dataCluster().lpush(redisAlipayQueue,JsonUtil.toJsonString(paymentRecordMonitorVo));
		}
		log.info("订单消息插入Redis队列结束...");
		// 发布消息
		log.info("订单消息发布到Redis...");
		JedisHelper.dataCluster().publish(redisChannel,JsonUtil.toJsonString(paymentRecordMonitorVo));
		log.info("订单消息发布到Redis结束...");
	}
3.2、监控服务
3.2.1.主要功能包括:
  • 订阅Redis中交易服务发布过来的订单消息以及获取list列表中的订单数据
  • 根据订单的交易时间,按照每15分钟为一个数据汇总点进行汇总
  • 对每15分钟汇总的SortedSet进行统计后,将结果再发布至Redis的消息中
3.2.2.遇到的坑:

在监控服务启动的时候会进行Redis的list列表中数据的统计初始化,并开启Redis消息订阅者的监听。但有三个比较坑的地方就是:

(1)因为用的是Redis6个节点组成的一个集群,所以是用JedisCluster,但是JedisCluster在2.8.x版本以上才支持消息的发布与订阅,项目原先用的是2.7.3版本 解决方案:把项目Jedis版本改为2.8.1,pom.xml内容如下:

     <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.1</version>
        </dependency>

(2)Redis的消息发布与订阅,在订阅方必须要手动的调用subscribe()方法,并将监听者和需要监听的通道作为参数传入才能开启监听,而像ActiveMQ这种消息中间件是不需要显示的调用,只需配置好消息监听者就会自动监听的。

还有一个坑就是subscribe()方法是一个线程阻塞方法,本想在项目启动的时候就调用subscribe()开启消息的订阅,结果发现方法调用后,其他的代码根本没法往下执行。 解决方案是:在项目启动的时候调用subscribe()方法开启消息监听,并且新开一个线程去调用subscribe()方法来避免阻塞主线程。

(3)Redis不支持消息的持久化。在订阅者没有启动的时候,消息发布者将消息发出去了,订阅者没有收到,那订阅者重新启动的时候也不会收到之前发的消息了,而像ActiveMQ是支持消息的持久化的。

解决方案:在往Redis发布消息的时候也同样往Redis的list列表中lpush一条同样消息的数据(参照上面交易服务中的代码),消息订阅者接收到消息并进行相应的业务处理后,再将list列表中的数据删除,那在监控服务挂掉的情况下,Redis消息无法正常被监听消费,但是Redis的list列表中还是会存有消息的数据,所以后续我们可以从list列表中取出消息数据再进行相应的业务处理,这样就间接的实现了Redis消息的持久化。

3.2.3.部分代码

(1)RedisSubscribeHelper.java:监控服务启动时,进行Redis队列中数据的统计初始化,并开启Redis消息订阅者的监听的

package com.ylp.core.monitor.redis;

import com.ylp.common.tools.utils.JsonUtil;
import com.ylp.core.monitor.biz.MonitorBiz;
import com.ylp.facade.monitor.utils.JedisHelper;
import com.ylp.facade.monitor.utils.MonitorUtils;
import com.ylp.facade.monitor.vo.PaymentRecordMonitorVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.fact
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值