Springboot与ActiveMQ、Solr、Redis中分布式事物的初步探索
Springboot与ActiveMQ、Solr、Redis中分布式事物的初步探索
- 解决的场景:事物中的异步问题,当要求数据库与solr服务器的最终一致时。
- 程序条件:
- 利用消息队列,当数据库添加成功时,将更新solr的请求发进消息队列中。
- 考虑到拿消息的服务做了集群的问题,利用redis做幂等性:将消息的JMSMessageID作为redis的key。
- 如果取消息的服务最终失败,就产生了需要补偿的问题。
- 解决方案:
- 将消息的参数存入redis中的Hash列表(结构:补偿的队列名 补偿的JMSMessageID(keys)以及对应的参数),做一个补偿系统,每隔一定时间扫描redis中补偿的队列名。
- 当redis中该hash列表不为空且长度大于0时,将整个hash列表的key序列化后放入消息队列中。
- 服务端的JMSListener扫描到目标队列有消息入队时,首先判断幂等性的问题;然后取出该消息,反序列化后遍历keys,并且通过redis读取对象添加到对应的表中。
- 若未补偿成功将消息写入分布式日志中。
代码:
首先是将添加solr的消息放入消息队列:
public AppResult AddItem(TbItem item, TbItemDesc itemDesc) {
IDUtils idUtils=new IDUtils();
Long itemId=idUtils.nextId();
Date date=new Date();
item.setId(itemId);
item.setCreated(date);
item.setUpdated(date);
itemDesc.setCreated(date);
itemDesc.setUpdated(date);
itemDesc.setItemId(itemId);
int ItemCount=itemMapper.insertSelective(item);
int ItemDescCount=itemDescMapper.insertSelective(itemDesc);
if (ItemCount!=1||ItemDescCount!=1){
throw new AppExeption(201,"添加失败");
}
SolrTbItem solrTbItem=new SolrTbItem();
solrTbItem.setItemTitle(item.getTitle());
solrTbItem.setItemCategoryName("123");
solrTbItem.setItemImage(item.getImage());
solrTbItem.setId(itemId);
solrTbItem.setItemPrice(item.getPrice());
solrTbItem.setItemSellPoint(item.getSellPoint());
//添加数据库
//添加到solr中,但需要通过消息队列
jmsTemplate.convertAndSend("tym_002",JsonUtils.objectToJson(solrTbItem));
return new AppResult(true,200,"添加成功",null);
}
初始消息监听器监听(集群):
@JmsListener(destination = "tym_002",containerFactory = "jmsQueryListenerFactory")
public void testreceiveQueue(TextMessage textMessage, Session session) throws Exception {
SolrTbItem solrTbItem=null;
try {
String id=textMessage.getJMSMessageID();
System.out.println(id);
if (redisTemplate.opsForValue().setIfAbsent(textMessage.getJMSMessageID(),"1")==false){
return;
}
solrTbItem= JsonUtils.jsonToPojo(textMessage.getText(),SolrTbItem.class);
int i=1/0;
AppResult result=searchService.addTbitem(solrTbItem);
if (result.isSuccess()==false){
throw new AppExeption();
}
System.out.println("成功添加");
textMessage.acknowledge();
redisTemplate.delete(textMessage.getJMSMessageID());
} catch (Exception e) {
System.out.println("存入redis 等待补偿系统");
redisTemplate.delete(textMessage.getJMSMessageID());
//补偿把item存到redis中,然后利用任务系统读取key keys values 中的keys传递给Jms补偿监听器
//把keys读取出来并遍历,然后调用服务添加到solr中。
redisTemplate.opsForHash().put("search_bc_add",textMessage.getJMSMessageID(),solrTbItem);
System.out.println("将整个消息队列放入redis的hash列表中,名为search_bc_add,等待消息系统取出补偿。。");
textMessage.acknowledge();
}
}
监听服务(监听任务):
@Scheduled(cron = "*/10 * * * * ?")
public void searchItemBcTask(){
//先判断redis里面有没有该队列
//取出该队列且根据长度和是否为空判断是否要发送补偿消息进补偿队列
Set<String> keys=redisTemplate.opsForHash().keys("search_bc_add");
if (keys!=null&&keys.size()>0){
System.out.println("发现有补偿消息");
jmsTemplate.convertAndSend("cs1901_bc_search_add", JsonUtils.objectToJson(keys));
}else {
System.out.println("无---------");
}
}
补偿监听(集群):
//该监听器监听补偿 即是第一个监听器未添加进solr成功,被任务系统查出,查找的消息队列
@JmsListener(destination = "cs1901_bc_search_add",containerFactory = "jmsQueryListenerFactory")
public void BcSearchAdd(TextMessage textMessage, Session session) throws Exception {
//首先考虑幂等性 问题,无则存入redis 有则return
//监听cs1901_bc_search_add 队列,若有则处理
// 处理方法,遍历keys,查询redis中存的tbitem 并调用searchService的方法插入solr索引库
//成功与否都删除redis中的键,全部键插入完成删除整个hash列表
//未成功打印到分布式日志中
try {
if (redisTemplate.hasKey(textMessage.getJMSMessageID())==true){
System.out.println("该数据被别的监听器锁住");
return;
}
System.out.println("将补偿消息锁住");
redisTemplate.opsForValue().set(textMessage.getJMSMessageID(),"");
Set keys=JsonUtils.jsonToPojo(textMessage.getText(), Set.class);
for (Object key : keys) {
SolrTbItem solrTbItem= (SolrTbItem) redisTemplate.opsForHash().get("search_bc_add",key);
System.out.println(solrTbItem.getId());
AppResult result=searchService.addTbitem(solrTbItem);
if (result.isSuccess()==false){
throw new AppExeption();
}
redisTemplate.opsForHash().delete("search_bc_add",key);
System.out.println("redis删除小键");
}
redisTemplate.delete("search_bc_add");
System.out.println("redis删除hash");
redisTemplate.delete(textMessage.getJMSMessageID());
System.out.println("redis删除该消息,解除锁");
textMessage.acknowledge();
}catch (Exception e){
//此处打印
textMessage.acknowledge();
}
}
第一次的监听服务的补偿暂时未做重试,由于考虑到定义次数的线程不安全性。
Springboot与ActiveMQ、Solr、Redis中分布式事物的初步探索的更多相关文章
- Redis 中的原子操作(3)-使用Redis实现分布式锁
Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...
- 使用redis 中的事务处理实现商品秒杀
redis中的事务处理: redis中的事物事物处理是指能够批量的执行一组命令(当事务开始执行时,事务中的命令能够按照按照规定好的顺序执行而不会被插队或打断): 与mysql事务的区别在于:mysql ...
- SpringBoot进阶教程(二十七)整合Redis之分布式锁
在之前的一篇文章(<Java分布式锁,搞懂分布式锁实现看这篇文章就对了>),已经介绍过几种java分布式锁,今天来个Redis分布式锁的demo.redis 现在已经成为系统缓存的必备组件 ...
- springboot(三):Spring boot中Redis的使用
spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...
- 在AspNetCore 中 使用Redis实现分布式缓存
AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemoryCache,以及缓存的基础概念.本篇会进行一些概念上的补充. 本篇我们记录的内容是怎么在Core中使 ...
- 在AspNetCore 中 使用Redis实现分布式缓存 (转载)
文章概念描述 分布式缓存描述:分布式缓存重点是在分布式上,相信大家接触过的分布式有很多中,像分布式开发,分布式部署,分布式锁.事物.系统 等有很多.使我们对分布式本身就有一个很明确的认识,分布式就是有 ...
- SpringBoot(三) :Spring boot 中 Redis 的使用
前言: 这一篇讲的是Spring Boot中Redis的运用,之前没有在项目中用过Redis,所以没有太大的感觉,以后可能需要回头再来仔细看看. 原文出处: 纯洁的微笑 SpringBoot对常用的数 ...
- 【转载】在AspNetCore 中 使用Redis实现分布式缓存
原文地址:https://www.cnblogs.com/szlblog/p/9045209.html AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemo ...
- SpringBoot电商项目实战 — Redis实现分布式锁
最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...
随机推荐
- 预训练中Word2vec,ELMO,GPT与BERT对比
预训练 先在某个任务(训练集A或者B)进行预先训练,即先在这个任务(训练集A或者B)学习网络参数,然后存起来以备后用.当我们在面临第三个任务时,网络可以采取相同的结构,在较浅的几层,网络参数可以直接加 ...
- 《ucore lab4》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1:分配并初始化一个进程控制块 题目 alloc_proc函数(位于kern/process/proc.c中) 负责分配并返回一个新的str ...
- 【转帖】docker 如何删除none镜像
https://blog.csdn.net/hicoldcat/article/details/80802447 shell 命令博大精深 需要仔细学习 删除none的镜像,要先删除镜像中的容器.要删 ...
- django使用pyecharts(3)----django加入echarts_定时全量更新
三.Django 前后端分离_定时全量更新图表 1.安装 djangorestframework linux pip3 install djangorestframework windows pip ...
- WUSTOJ 1305: 最短路(Java)
题目链接:
- 【springcloud】1.微服务之springcloud-》eureka源码分析之请叫我灵魂画师。。。
- mysql创建唯一索引UNIQUE INDEX,以及报错“#失败原因: [Execute: Duplicate entry '733186700' for key 'uniq_video_id_index']”
要给t_video_prods表的video_id字段创建唯一所以,可以使用下面这条语句: alter table t_video_prods add UNIQUE INDEX `uniq_video ...
- ubuntu修改密码
ubuntu修改密码 本文链接:https://blog.csdn.net/heybob/article/details/9095727 修改root密码: 1,$sudo su,输入密码进入root ...
- interface Part4(接口中的多态)
使用接口实现多态 需要满足以下两个条件. 定义接口并使用类实现了接口中的成员. 创建接口的实例指向不同的实现类对象. 假设接口名称为 ITest,分别定义两个实现类来实现接口的成员,示例代码如下. i ...
- c#基础知识梳理(一)
一.C#简介 C#是微软公司发布的一种面向对象的.运行于.NET Framework之上的高级程序设计语言.C#看起来与Java有着惊人的相似:它包括了诸如单一继承.接口.与Java几乎同样的语法和编 ...