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. 用pytorch1.0搭建简单的神经网络:进行多分类分析

    用pytorch1.0搭建简单的神经网络:进行多分类分析 import torch import torch.nn.functional as F # 包含激励函数 import matplotlib ...

  2. python安装OpenCV – 4.1.0

    (python3) [jiangshan@localhost ~]$ pip install opencv_python==4.1.0Collecting opencv_python==4.1.0 E ...

  3. [SourceTree] - 使用内置 Git 克隆项目出现 templates not found 问题之解决

    背景 使用 SourceTree 克隆 Asp.Net Core 项目失败. 错误 warning: templates not found C:\Program Files\Git\share\gi ...

  4. EFCore中的导航属性

    使用了这么久的EntityFrameworkCore框架,今天想来就其中的一个部分来做一个知识的梳理,从而使自己对于整个知识有一个更加深入的理解,如果你对EFCore中的实体关系不熟悉你需要有一个知识 ...

  5. 【转】MySQL中EXISTS的用法

    原文链接:https://www.cnblogs.com/qlqwjy/p/8598091.html 比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,Compan ...

  6. Python 容器用法整理

    本文整理几种基本容器:列表.元组.字典和集合的用法和collections中几种已经预先实现的容器数据结构:namedtuple(),双向链表deque,ChainMap,Counter,Ordere ...

  7. hdu 2167 题解

    题目 题意 一个数字正方形(所有数都是两位的正整数),取了一个数后,它的周围 $ 8 $ 个数都不能被选,问最大取数总和. 注意数据范围 $ 3=< n <=15 $ 我们可能一开始会去想 ...

  8. 1185: 零起点学算法92——单词数(C)

    一.题目 http://acm.wust.edu.cn/problem.php?id=1185&soj=0 二.分析 统计的是不同的单词数,即重复的单词只统计一次: 多组输入: 每行不超过10 ...

  9. MySQL新项目如何确保上线安全

    大纲 DBA最应该做的事情 新项目开发环境应该注意什么 功能测试和压力测试MySQL DBA关注点 线上环境关注点 业务在大压力情况下,MySQL如何能活下来 DBA最应该做的事情 备份 建议每天全备 ...

  10. Kafka 的这些原理你知道吗

    如果只是为了开发 Kafka 应用程序,或者只是在生产环境使用 Kafka,那么了解 Kafka 的内部工作原理不是必须的.不过,了解 Kafka 的内部工作原理有助于理解 Kafka 的行为,也利用 ...