• 在库存服务中实现缓存与数据库双写一致性保障方案(三)


    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);
       }
    
    }
    

      

     

  • 相关阅读:
    hadoop-2.7.3安装kafka_2.11-2.1.0
    HBase数据快速导入之ImportTsv&Bulkload
    hbase shell 基本操作
    SQLplus命令中删除键和翻页键不能用的问题
    SQL*Loader 的使用sqlldr和sqluldr2方法详解
    python连接oracle导出数据文件
    python零碎知识点一
    用Python输出一个Fibonacci数列
    问题总结——window平台下gruntower安装后无法运行的问题
    JavaScript学习笔记——浅拷贝、深拷贝
  • 原文地址:https://www.cnblogs.com/q1359720840/p/15867936.html
Copyright © 2020-2023  润新知