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 ...
随机推荐
- 图解JavaScript闭包面试题
由于最近在学习关于闭包相关的知识,并且闭包这个知识点让我有点搞不太清楚其具体的定义,所以在网上也查阅了很多大佬的讲解和对闭包的一个定义. 最后感觉还是MDN上的说法感觉比较好理解一些,对闭包还是不太理 ...
- Django 高级视图
一.Django限制请求method 常用的请求method: GET请求:GET请求一般用来向服务器索取数据,但不会向服务器提交数据,不会对服务器的状态进行更改.比如向服务器获取某篇文章的详情. P ...
- sql 查找最晚入职员工
题目描述 查找最晚入职员工的所有信息CREATE TABLE `employees` (`emp_no` int(11) NOT NULL,`birth_date` date NOT NULL,`fi ...
- 深入理解Flink核心技术及原理
前言 Apache Flink(下简称Flink)项目是大数据处理领域最近冉冉升起的一颗新星,其不同于其他大数据项目的诸多特性吸引了越来越多人的关注.本文将深入分析Flink的一些关键技术与特性,希望 ...
- 037 Android Glide图片加载开源框架使用
1.Glide简单介绍 Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片.Glide是一个快速高效的Androi ...
- 实现不同分辨率、不同浏览器下高度自适应、iframe高度自适应
html: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...
- Burp Suite的安装与使用
Burp Suite是一个集成化的渗透测试工具,它集合了多种渗透测试组件,使我们自动化地或手工地能更好的完成对web应用的渗透测试和攻击.在渗透测试中,我们使用Burp Suite将使得测试工作变 ...
- 05 IO流(三)——IO流标准流程
流程 选择源 选择合适的流 操作 关闭流:先打开的后关闭 演示 import java.io.File; import java.io.InputStream; import java.io.File ...
- xorm表结构操作实例
获取数据库信息 package main import ( "fmt" _ "github.com/go-sql-driver/mysql" "git ...
- PB笔记之导入、导出组件
导入组件 导出组件