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

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

}

  

 

 

posted @ 2022-02-07 15:05  三号小玩家  阅读(41)  评论(0编辑  收藏  举报
Title
三号小玩家的 Mail: 17612457115@163.com, 联系QQ: 1359720840 微信: QQ1359720840