在库存服务中实现缓存与数据库双写一致性保障方案(三)
public interface Request {
void process();
Integer getProductId();
}
接口增加一个获取id的方法。
请求内存队列,做一个统一的入口和出口,需要单例
package com.roncoo.eshop.req;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 请求内存队列
* @author Administrator
*
*/
public class RequestQueue {
/**
* 内存队列
*/
private List<ArrayBlockingQueue<Request>> queues = new ArrayList<ArrayBlockingQueue<Request>>();
/**
* 单例有很多种方式去实现:我采取绝对线程安全的一种方式
*
* 静态内部类的方式,去初始化单例
*
* @author Administrator
*
*/
private static class Singleton {
private static RequestQueue instance;
static {
instance = new RequestQueue();
}
public static RequestQueue getInstance() {
return instance;
}
}
/**
* jvm的机制去保证多线程并发安全
*
* 内部类的初始化,一定只会发生一次,不管多少个线程并发去初始化
*
* @return
*/
public static RequestQueue getInstance() {
return Singleton.getInstance();
}
/**
* 添加一个内存队列
* @param queue
*/
public void addQueue(ArrayBlockingQueue<Request> queue) {
this.queues.add(queue);
}
/**
* 获取内存队列的数量
* @return
*/
public int queueSize() {
return queues.size();
}
/**
* 获取内存队列
* @param index
* @return
*/
public ArrayBlockingQueue<Request> getQueue(int index) {
return queues.get(index);
}
}

项目初始化就把队列数据初始化好。
package com.roncoo.eshop.service;
import com.roncoo.eshop.req.Request;
import com.roncoo.eshop.req.RequestQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class RequestAsyncProcessServiceImpl implements RequestAsyncProcessService {
/***
*做请求的路由,根据每个请求的商品id,路由到对应的内存队列中去
* @param request
*/
@Override
public void process(Request request) {
try {
/**
* 根据id去hash集合中的位置,获取到位置的queue,把数据加到queue中。
*/
ArrayBlockingQueue<Request> routingQueue =
getRoutingQueue(request.getProductId());
routingQueue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取路由到的内存队列
*/
public ArrayBlockingQueue<Request> getRoutingQueue(Integer productId) {
RequestQueue requestQueue = RequestQueue.getInstance();
String key = String.valueOf(productId);
int h;
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
// //得到的hash值
int index = (requestQueue.queueSize() - 1) & hash;
return requestQueue.getQueue(index);
}
}
增加一个service的方法,从缓存获取库存的方法。
/**
* 获取缓存中的库存数量
* @param productId
* @return
*/
@Override
public ProductInventory getProductInventoryCache(Integer productId) {
Long inventoryCnt =0L;
String key= "product:inventory:"+productId;
String result = redisDAO.get(key);
if (result != null&&!"".equals(result)){
inventoryCnt = Long.valueOf(result);
return new ProductInventory(productId,inventoryCnt);
}
return null;
}
更新商品库存的时候,把请求加到异步请求队列中
查询商品库存的时候,加到异步请求的队列的路由中, 如果规定时间内没查到缓存数据,就查询数据库
package com.roncoo.eshop.controller;
import com.roncoo.eshop.entity.ProductInventory;
import com.roncoo.eshop.entity.Response;
import com.roncoo.eshop.req.DataUpdateRequest;
import com.roncoo.eshop.req.ProductInventoryCacheRefreshRequest;
import com.roncoo.eshop.req.Request;
import com.roncoo.eshop.service.ProductInventoryService;
import com.roncoo.eshop.service.RequestAsyncProcessService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
/**
* 商品库存Controller
* @author Administrator
*
*/
@Controller
public class ProductInventoryController {
@Resource
private RequestAsyncProcessService requestAsyncProcessService;
@Resource
private ProductInventoryService productInventoryService;
/**
* 更新商品库存
*/
@RequestMapping("/updateProductInventory")
@ResponseBody
public Response updateProductInventory(ProductInventory productInventory) {
Response response = null;
try {
//商品id,商品数量。商品库存Service接口
Request request = new DataUpdateRequest(
productInventory, productInventoryService);
requestAsyncProcessService.process(request);
/**
* 返回状态
*/
response = new Response(Response.SUCCESS);
} catch (Exception e) {
e.printStackTrace();
response = new Response(Response.FAILURE);
}
return response;
}
/**
* 获取商品库存
*/
@RequestMapping("/getProductInventory")
@ResponseBody
public ProductInventory getProductInventory(Integer productId) {
ProductInventory inventory = null;
try {
ProductInventoryCacheRefreshRequest request = new ProductInventoryCacheRefreshRequest(productId, productInventoryService);
//把数据加到内存队列中
requestAsyncProcessService.process(request);
//开始时间,结束时间
long startTime = System.currentTimeMillis();
Long endTime =0L;
Long waitTime =0L;
while (true){
if(waitTime >200){
break;
}
//尝试redis读取缓存数据
inventory = productInventoryService.getProductInventoryCache(productId);
if(inventory !=null){
return inventory;
}else{
Thread.sleep(20);
endTime = System.currentTimeMillis();
waitTime = endTime - startTime;
}
}
// 直接尝试从数据库中读取数据
inventory = productInventoryService.findProductInventory(productId);
if(inventory !=null){
return inventory;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ProductInventory(productId, -1L);
}
}

---------------------------------------------------------------------------
国之殇,未敢忘!
南京大屠杀!
731部队!
(有关书籍《恶魔的饱食》)以及核污染水排海等一系列全无人性的操作,购买他们的食品和为它们提供帮助只会更加变本加厉的害你,呼吁大家不要购买日本相关产品
昭昭前事,惕惕后人
吾辈当自强,方使国不受他人之侮!
---------------------------------------------------------------------------
作者:三号小玩家
出处:https://www.cnblogs.com/q1359720840/
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 版权信息

浙公网安备 33010602011771号