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中分布式事物的初步探索的更多相关文章

  1. Redis 中的原子操作(3)-使用Redis实现分布式锁

    Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...

  2. 使用redis 中的事务处理实现商品秒杀

    redis中的事务处理: redis中的事物事物处理是指能够批量的执行一组命令(当事务开始执行时,事务中的命令能够按照按照规定好的顺序执行而不会被插队或打断): 与mysql事务的区别在于:mysql ...

  3. SpringBoot进阶教程(二十七)整合Redis之分布式锁

    在之前的一篇文章(<Java分布式锁,搞懂分布式锁实现看这篇文章就对了>),已经介绍过几种java分布式锁,今天来个Redis分布式锁的demo.redis 现在已经成为系统缓存的必备组件 ...

  4. springboot(三):Spring boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  5. 在AspNetCore 中 使用Redis实现分布式缓存

    AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemoryCache,以及缓存的基础概念.本篇会进行一些概念上的补充. 本篇我们记录的内容是怎么在Core中使 ...

  6. 在AspNetCore 中 使用Redis实现分布式缓存 (转载)

    文章概念描述 分布式缓存描述:分布式缓存重点是在分布式上,相信大家接触过的分布式有很多中,像分布式开发,分布式部署,分布式锁.事物.系统 等有很多.使我们对分布式本身就有一个很明确的认识,分布式就是有 ...

  7. SpringBoot(三) :Spring boot 中 Redis 的使用

    前言: 这一篇讲的是Spring Boot中Redis的运用,之前没有在项目中用过Redis,所以没有太大的感觉,以后可能需要回头再来仔细看看. 原文出处: 纯洁的微笑 SpringBoot对常用的数 ...

  8. 【转载】在AspNetCore 中 使用Redis实现分布式缓存

    原文地址:https://www.cnblogs.com/szlblog/p/9045209.html AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemo ...

  9. SpringBoot电商项目实战 — Redis实现分布式锁

    最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...

随机推荐

  1. 关于mathtype6.9在office2010中出现The MathType can not be found的问题

    本文问题解决方法参考:https://blog.csdn.net/yiran103/article/details/41694843 自从重装了系统,安装mathtype总是提示The MathTyp ...

  2. console.log()和alert()的区别

    一直都是知道console.log()和alert()是有区别的,但是具体有什么区别就不清楚了,后来在权威指南里注意到了说alert()具有侵入性才来查一查两者的具体区别. 查询到的区别: alert ...

  3. Ubuntu18.04 安装MySQL

    安装 #命令1 sudo apt-get update #命令2 sudo apt-get install mysql-server 初始化 sudo mysql_secure_installatio ...

  4. 创建一个用目录分层的Rust应用

    一:前言,这是Rust基础程序,主要是用来讲解怎么创建分层(类似Java package)的应用: 二:代码实现: 2.1在src下创建main.rs,然后声明main方法,代码如下: fn main ...

  5. MySQL引擎类型(三)

    InnoDB: 1)经常更新的表,适合处理多重并发的更新请求. 2)支持事务. 3)可以从灾难中恢复(通过bin-log日志等). 4)外键约束.只有他支持外键. 5)支持自动增加列属性auto_in ...

  6. todo---java中的json探讨

    1.json的命名格式 2.json赋值原则 3.json常用的工具 4.json的处理的第三方软件比较 5.json的起源 6.关于json串的对于null ,"" 的不同的第三 ...

  7. 阿里云服务器安装mysql8遇到的问题

    1.提供一个离线mysql下载路径:http://mirrors.163.com/mysql/Downloads/MySQL-8.0/ 2.在线mysql8安装: 2.1 下载mysql: wget ...

  8. Python入门学习——PyQt5程序基本结构

    在学习python GUI部分时,一开始看书有点懵,看不懂框架,以下是个人学习所得(参考了别人的视频讲解),错误之处,望大家指教 #0.导入需要的包和模块from PyQt5.Qt import * ...

  9. 玫瑰花小制作分享-JavaScript(七夕专属浪漫)

    分享一个玫瑰花的制作小方法,用小小的代码给自己的她送上一个不一样的玫瑰花. 玫瑰花代码由JavaScript实现,JavaScript 作为一种脚本语言, 被发明用于在 HTML 网页上使用,可以给H ...

  10. youku项目总结(粗略总结)

    一.ORM 之前我们都是以文件保存的形式存储数据,这次我们用的是数据库结合python使用,用到 ORM:关系型映射 类>>数据库的一张表 对象>>表一条记录 对象.属性> ...