基于rocketMq秒杀系统demo

基于RocketMQ设计秒杀。

要求:

1.     秒杀商品LagouPhone,数量100个。

2.     秒杀商品不能超卖。

3.     抢购链接隐藏

4.     Nginx+Redis+RocketMQ+Tomcat+MySQL

 

 

实现

接口说明:https://www.liuchengtu.com/swdt/#R9f978d0d00ef9be99f00258c3035b648

实现代码:

订单接口:

package com.lagou.rocket.controller;import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.RateLimiter;
import com.lagou.rocket.service.OrderService;
import com.lagou.rocket.service.StockService;
import com.lagou.rocket.service.UserService;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Controller
public class OrderController {private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);@Autowiredprivate OrderService orderService;@Autowiredprivate UserService userService;@Autowiredprivate StockService stockService;@Autowiredprivate RocketMQTemplate rocketMQTemplate;// Guava令牌桶:每秒放行10个请求RateLimiter rateLimiter = RateLimiter.create(10);// 延时时间:预估读数据库数据业务逻辑的耗时,用来做缓存再删除private static final int DELAY_MILLSECONDS = 1000;// 延时双删线程池private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());/*** 下单接口:导致超卖的错误示范* @param sid* @return*/@RequestMapping("/createWrongOrder/{sid}")@ResponseBodypublic String createWrongOrder(@PathVariable int sid) {int id = 0;try {id = orderService.createWrongOrder(sid);LOGGER.info("创建订单id: [{}]", id);} catch (Exception e) {LOGGER.error("Exception", e);}return String.valueOf(id);}/*** 下单接口:乐观锁更新库存 + 令牌桶限流* @param sid* @return*/@RequestMapping("/createOptimisticOrder/{sid}")@ResponseBodypublic String createOptimisticOrder(@PathVariable int sid) {// 1. 阻塞式获取令牌LOGGER.info("等待时间" + rateLimiter.acquire());// 2. 非阻塞式获取令牌
//        if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
//            LOGGER.warn("你被限流了,真不幸,直接返回失败");
//            return "你被限流了,真不幸,直接返回失败";
//        }int id;try {id = orderService.createOptimisticOrder(sid);LOGGER.info("购买成功,剩余库存为: [{}]", id);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}return String.format("购买成功,剩余库存为:%d", id);}/*** 下单接口:悲观锁更新库存 事务for update更新库存* @param sid* @return*/@RequestMapping("/createPessimisticOrder/{sid}")@ResponseBodypublic String createPessimisticOrder(@PathVariable int sid) {int id;try {id = orderService.createPessimisticOrder(sid);LOGGER.info("购买成功,剩余库存为: [{}]", id);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}return String.format("购买成功,剩余库存为:%d", id);}/*** 验证接口:下单前用户获取验证值* @return*/@RequestMapping(value = "/getVerifyHash", method = {RequestMethod.GET})@ResponseBodypublic String getVerifyHash(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId) {String hash;try {hash = userService.getVerifyHash(sid, userId);} catch (Exception e) {LOGGER.error("获取验证hash失败,原因:[{}]", e.getMessage());return "获取验证hash失败";}return hash;}/*** 下单接口:要求用户验证的抢购接口* @param sid* @return*/@RequestMapping(value = "/createOrderWithVerifiedUrl", method = {RequestMethod.GET})@ResponseBodypublic String createOrderWithVerifiedUrl(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId,@RequestParam(value = "verifyHash") String verifyHash) {int stockLeft;try {stockLeft = orderService.createVerifiedOrder(sid, userId, verifyHash);LOGGER.info("购买成功,剩余库存为: [{}]", stockLeft);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return e.getMessage();}return "ok";}/*** 下单接口:要求用户验证的抢购接口* @param sid* @return*/@RequestMapping(value = "/pay", method = {RequestMethod.GET})@ResponseBodypublic String pay(@RequestParam(value = "sid") Integer sid, @RequestParam(value = "userId") Integer userId) {return orderService.pay(sid,userId);}/*** 下单接口:要求验证的抢购接口 + 单用户限制访问频率* @param sid* @return*/@RequestMapping(value = "/createOrderWithVerifiedUrlAndLimit", method = {RequestMethod.GET})@ResponseBodypublic String createOrderWithVerifiedUrlAndLimit(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId,@RequestParam(value = "verifyHash") String verifyHash) {int stockLeft;try {int count = userService.addUserCount(userId);LOGGER.info("用户截至该次的访问次数为: [{}]", count);boolean isBanned = userService.getUserIsBanned(userId);if (isBanned) {return "购买失败,超过频率限制";}stockLeft = orderService.createVerifiedOrder(sid, userId, verifyHash);LOGGER.info("购买成功,剩余库存为: [{}]", stockLeft);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return e.getMessage();}return String.format("购买成功,剩余库存为:%d", stockLeft);}/*** 下单接口:先删除缓存,再更新数据库* @param sid* @return*/@RequestMapping("/createOrderWithCacheV1/{sid}")@ResponseBodypublic String createOrderWithCacheV1(@PathVariable int sid) {int count = 0;try {// 删除库存缓存stockService.delStockCountCache(sid);// 完成扣库存下单事务orderService.createPessimisticOrder(sid);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}LOGGER.info("购买成功,剩余库存为: [{}]", count);return String.format("购买成功,剩余库存为:%d", count);}/*** 下单接口:先更新数据库,再删缓存* @param sid* @return*/@RequestMapping("/createOrderWithCacheV2/{sid}")@ResponseBodypublic String createOrderWithCacheV2(@PathVariable int sid) {int count = 0;try {// 完成扣库存下单事务orderService.createPessimisticOrder(sid);// 删除库存缓存stockService.delStockCountCache(sid);} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}LOGGER.info("购买成功,剩余库存为: [{}]", count);return String.format("购买成功,剩余库存为:%d", count);}/*** 下单接口:先删除缓存,再更新数据库,缓存延时双删* @param sid* @return*/@RequestMapping("/createOrderWithCacheV3/{sid}")@ResponseBodypublic String createOrderWithCacheV3(@PathVariable int sid) {int count;try {// 删除库存缓存stockService.delStockCountCache(sid);// 完成扣库存下单事务count = orderService.createPessimisticOrder(sid);LOGGER.info("完成下单事务");// 延时指定时间后再次删除缓存cachedThreadPool.execute(new delCacheByThread(sid));} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}LOGGER.info("购买成功,剩余库存为: [{}]", count);return String.format("购买成功,剩余库存为:%d", count);}/*** 下单接口:先更新数据库,再删缓存,删除缓存失败重试,通知消息队列* @param sid* @return*/@RequestMapping("/createOrderWithCacheV4/{sid}")@ResponseBodypublic String createOrderWithCacheV4(@PathVariable int sid) {int count;try {// 完成扣库存下单事务count = orderService.createPessimisticOrder(sid);LOGGER.info("完成下单事务");// 删除库存缓存stockService.delStockCountCache(sid);// 延时指定时间后再次删除缓存// cachedThreadPool.execute(new delCacheByThread(sid));// 假设上述再次删除缓存没成功,通知消息队列进行删除缓存sendToDelCache(String.valueOf(sid));} catch (Exception e) {LOGGER.error("购买失败:[{}]", e.getMessage());return "购买失败,库存不足";}LOGGER.info("购买成功,剩余库存为: [{}]", count);return "购买成功";}/*** 下单接口:异步处理订单* @param sid* @return*/@RequestMapping(value = "/createOrderWithMq", method = {RequestMethod.GET})@ResponseBodypublic String createOrderWithMq(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId) {try {// 检查缓存中商品是否还有库存Integer count = stockService.getStockCount(sid);if (count == 0) {return "秒杀请求失败,库存不足.....";}// 有库存,则将用户id和商品id封装为消息体传给消息队列处理// 注意这里的有库存和已经下单都是缓存中的结论,存在不可靠性,在消息队列中会查表再次验证LOGGER.info("有库存:[{}]", count);JSONObject jsonObject = new JSONObject();jsonObject.put("sid", sid);jsonObject.put("userId", userId);sendToOrderQueue(jsonObject.toJSONString());return "秒杀请求提交成功";} catch (Exception e) {LOGGER.error("下单接口:异步处理订单异常:", e);return "秒杀请求失败,服务器正忙.....";}}/*** 下单接口:异步处理订单* @param sid* @return*/@RequestMapping(value = "/createUserOrderWithMq", method = {RequestMethod.GET})@ResponseBodypublic String createUserOrderWithMq(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId) {try {// 检查缓存中该用户是否已经下单过Boolean hasOrder = orderService.checkUserOrderInfoInCache(sid, userId);if (hasOrder != null && hasOrder) {LOGGER.info("该用户已经抢购过");return "你已经抢购过了,不要太贪心.....";}// 没有下单过,检查缓存中商品是否还有库存LOGGER.info("没有抢购过,检查缓存中商品是否还有库存");Integer count = stockService.getStockCount(sid);if (count == 0) {return "秒杀请求失败,库存不足.....";}// 有库存,则将用户id和商品id封装为消息体传给消息队列处理// 注意这里的有库存和已经下单都是缓存中的结论,存在不可靠性,在消息队列中会查表再次验证LOGGER.info("有库存:[{}]", count);JSONObject jsonObject = new JSONObject();jsonObject.put("sid", sid);jsonObject.put("userId", userId);sendToOrderQueue(jsonObject.toJSONString());return "秒杀请求提交成功";} catch (Exception e) {LOGGER.error("下单接口:异步处理订单异常:", e);return "秒杀请求失败,服务器正忙.....";}}/*** 检查缓存中用户是否已经生成订单* @param sid* @return*/@RequestMapping(value = "/checkOrderByUserIdInCache", method = {RequestMethod.GET})@ResponseBodypublic String checkOrderByUserIdInCache(@RequestParam(value = "sid") Integer sid,@RequestParam(value = "userId") Integer userId) {// 检查缓存中该用户是否已经下单过try {Boolean hasOrder = orderService.checkUserOrderInfoInCache(sid, userId);if (hasOrder != null && hasOrder) {return "恭喜您,已经抢购成功!";}} catch (Exception e) {LOGGER.error("检查订单异常:", e);}return "很抱歉,你的订单尚未生成,继续排队。";}/*** 缓存再删除线程*/private class delCacheByThread implements Runnable {private int sid;public delCacheByThread(int sid) {this.sid = sid;}public void run() {try {LOGGER.info("异步执行缓存再删除,商品id:[{}], 首先休眠:[{}] 毫秒", sid, DELAY_MILLSECONDS);Thread.sleep(DELAY_MILLSECONDS);stockService.delStockCountCache(sid);LOGGER.info("再次删除商品id:[{}] 缓存", sid);} catch (Exception e) {LOGGER.error("delCacheByThread执行出错", e);}}}/*** 向消息队列delCache发送消息* @param message*/private void sendToDelCache(String message) {LOGGER.info("这就去通知消息队列开始重试删除缓存:[{}]", message);
//        this.rabbitTemplate.convertAndSend("delCache", message);// 用于向broker发送消息// 第一个参数是topic名称// 第二个参数是消息内容this.rocketMQTemplate.convertAndSend("tp_springboot_01",message);}/*** 向消息队列orderQueue发送消息* @param message*/private void sendToOrderQueue(String message) {LOGGER.info("这就去通知消息队列开始下单:[{}]", message);
//        this.rabbitTemplate.convertAndSend("orderQueue", message);// 用于向broker发送消息// 第一个参数是topic名称// 第二个参数是消息内容this.rocketMQTemplate.convertAndSend("tp_springboot_01",message);}@GetMapping("/send")@ResponseBodypublic void testSendMessage() {// 用于向broker发送消息// 第一个参数是topic名称// 第二个参数是消息内容this.rocketMQTemplate.convertAndSend("tp_springboot_01","springboot: hello lagou2222222222222222");}}

库存接口:

package com.lagou.rocket.controller;import com.lagou.rocket.service.StockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class StockController {private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);@Autowiredprivate StockService stockService;/*** 查询库存:通过数据库查询库存* @param sid* @return*/@RequestMapping("/getStockByDB/{sid}")@ResponseBodypublic String getStockByDB(@PathVariable int sid) {int count;try {count = stockService.getStockCountByDB(sid);} catch (Exception e) {LOGGER.error("查询库存失败:[{}]", e.getMessage());return "查询库存失败";}LOGGER.info("商品Id: [{}] 剩余库存为: [{}]", sid, count);return String.format("商品Id: %d 剩余库存为:%d", sid, count);}/*** 查询库存:通过缓存查询库存* 缓存命中:返回库存* 缓存未命中:查询数据库写入缓存并返回* @param sid* @return*/@RequestMapping("/getStockByCache/{sid}")@ResponseBodypublic String getStockByCache(@PathVariable int sid) {Integer count;try {count = stockService.getStockCount(sid);} catch (Exception e) {LOGGER.error("查询库存失败:[{}]", e.getMessage());return "查询库存失败";}LOGGER.info("商品Id: [{}] 剩余库存为: [{}]", sid, count);return String.format("商品Id: %d 剩余库存为:%d", sid, count);}@RequestMapping("/static/home")public String toHome(Model model){return "home";}
}

业务处理:

订单服务:

package com.lagou.rocket.service.impl;import com.lagou.rocket.entity.Stock;
import com.lagou.rocket.entity.User;
import com.lagou.rocket.mapper.UserMapper;
import com.lagou.rocket.service.StockService;
import com.lagou.rocket.service.UserService;
import com.lagou.rocket.utils.CacheKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl implements UserService {private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);private static final String SALT = "randomString";private static final int ALLOW_COUNT = 10;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate UserMapper userMapper;@Autowiredprivate StockService stockService;@Overridepublic String getVerifyHash(Integer sid, Integer userId) throws Exception {// 验证是否在抢购时间内
//        LOGGER.info("请自行验证是否在抢购时间内");// 检查用户合法性User user = userMapper.selectByPrimaryKey(userId.longValue());if (user == null) {throw new Exception("用户不存在");}LOGGER.info("用户信息:[{}]", user.toString());// 检查商品合法性Stock stock = stockService.getStockById(sid);if (stock == null) {throw new Exception("商品不存在");}LOGGER.info("商品信息:[{}]", stock.toString());// 生成hashString verify = SALT + sid + userId;String verifyHash = DigestUtils.md5DigestAsHex(verify.getBytes());// 将hash和用户商品信息存入redisString hashKey = CacheKey.HASH_KEY.getKey() + "_" + sid + "_" + userId;stringRedisTemplate.opsForValue().set(hashKey, verifyHash, 3600, TimeUnit.SECONDS);LOGGER.info("Redis写入:[{}] [{}]", hashKey, verifyHash);return verifyHash;}@Overridepublic int addUserCount(Integer userId) throws Exception {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);int limit = -1;if (limitNum == null) {stringRedisTemplate.opsForValue().set(limitKey, "0", 3600, TimeUnit.SECONDS);} else {limit = Integer.parseInt(limitNum) + 1;stringRedisTemplate.opsForValue().set(limitKey, String.valueOf(limit), 3600, TimeUnit.SECONDS);}return limit;}@Overridepublic boolean getUserIsBanned(Integer userId) {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);if (limitNum == null) {LOGGER.error("该用户没有访问申请验证值记录,疑似异常");return true;}return Integer.parseInt(limitNum) > ALLOW_COUNT;}
}

用户服务:

package com.lagou.rocket.service.impl;import com.lagou.rocket.entity.Stock;
import com.lagou.rocket.entity.User;
import com.lagou.rocket.mapper.UserMapper;
import com.lagou.rocket.service.StockService;
import com.lagou.rocket.service.UserService;
import com.lagou.rocket.utils.CacheKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl implements UserService {private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);private static final String SALT = "randomString";private static final int ALLOW_COUNT = 10;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate UserMapper userMapper;@Autowiredprivate StockService stockService;@Overridepublic String getVerifyHash(Integer sid, Integer userId) throws Exception {// 验证是否在抢购时间内
//        LOGGER.info("请自行验证是否在抢购时间内");// 检查用户合法性User user = userMapper.selectByPrimaryKey(userId.longValue());if (user == null) {throw new Exception("用户不存在");}LOGGER.info("用户信息:[{}]", user.toString());// 检查商品合法性Stock stock = stockService.getStockById(sid);if (stock == null) {throw new Exception("商品不存在");}LOGGER.info("商品信息:[{}]", stock.toString());// 生成hashString verify = SALT + sid + userId;String verifyHash = DigestUtils.md5DigestAsHex(verify.getBytes());// 将hash和用户商品信息存入redisString hashKey = CacheKey.HASH_KEY.getKey() + "_" + sid + "_" + userId;stringRedisTemplate.opsForValue().set(hashKey, verifyHash, 3600, TimeUnit.SECONDS);LOGGER.info("Redis写入:[{}] [{}]", hashKey, verifyHash);return verifyHash;}@Overridepublic int addUserCount(Integer userId) throws Exception {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);int limit = -1;if (limitNum == null) {stringRedisTemplate.opsForValue().set(limitKey, "0", 3600, TimeUnit.SECONDS);} else {limit = Integer.parseInt(limitNum) + 1;stringRedisTemplate.opsForValue().set(limitKey, String.valueOf(limit), 3600, TimeUnit.SECONDS);}return limit;}@Overridepublic boolean getUserIsBanned(Integer userId) {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);if (limitNum == null) {LOGGER.error("该用户没有访问申请验证值记录,疑似异常");return true;}return Integer.parseInt(limitNum) > ALLOW_COUNT;}
}

库存服务:

package com.lagou.rocket.service.impl;import com.lagou.rocket.entity.Stock;
import com.lagou.rocket.entity.User;
import com.lagou.rocket.mapper.UserMapper;
import com.lagou.rocket.service.StockService;
import com.lagou.rocket.service.UserService;
import com.lagou.rocket.utils.CacheKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl implements UserService {private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);private static final String SALT = "randomString";private static final int ALLOW_COUNT = 10;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate UserMapper userMapper;@Autowiredprivate StockService stockService;@Overridepublic String getVerifyHash(Integer sid, Integer userId) throws Exception {// 验证是否在抢购时间内
//        LOGGER.info("请自行验证是否在抢购时间内");// 检查用户合法性User user = userMapper.selectByPrimaryKey(userId.longValue());if (user == null) {throw new Exception("用户不存在");}LOGGER.info("用户信息:[{}]", user.toString());// 检查商品合法性Stock stock = stockService.getStockById(sid);if (stock == null) {throw new Exception("商品不存在");}LOGGER.info("商品信息:[{}]", stock.toString());// 生成hashString verify = SALT + sid + userId;String verifyHash = DigestUtils.md5DigestAsHex(verify.getBytes());// 将hash和用户商品信息存入redisString hashKey = CacheKey.HASH_KEY.getKey() + "_" + sid + "_" + userId;stringRedisTemplate.opsForValue().set(hashKey, verifyHash, 3600, TimeUnit.SECONDS);LOGGER.info("Redis写入:[{}] [{}]", hashKey, verifyHash);return verifyHash;}@Overridepublic int addUserCount(Integer userId) throws Exception {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);int limit = -1;if (limitNum == null) {stringRedisTemplate.opsForValue().set(limitKey, "0", 3600, TimeUnit.SECONDS);} else {limit = Integer.parseInt(limitNum) + 1;stringRedisTemplate.opsForValue().set(limitKey, String.valueOf(limit), 3600, TimeUnit.SECONDS);}return limit;}@Overridepublic boolean getUserIsBanned(Integer userId) {String limitKey = CacheKey.LIMIT_KEY.getKey() + "_" + userId;String limitNum = stringRedisTemplate.opsForValue().get(limitKey);if (limitNum == null) {LOGGER.error("该用户没有访问申请验证值记录,疑似异常");return true;}return Integer.parseInt(limitNum) > ALLOW_COUNT;}
}

缓存服务:

package com.lagou.rocket.service.impl;import com.alibaba.fastjson.JSON;
import com.lagou.rocket.service.JedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.GeoRadiusResponse;
import redis.clients.jedis.JedisCluster;import java.util.List;
import java.util.Map;@Service
public class JedisServiceImpl implements JedisService {@Autowiredprivate JedisCluster jedisCluster;@Overridepublic boolean exists(String key) {boolean flag = false;flag = jedisCluster.exists(key);return flag;}@Overridepublic String set(String key, String value, int seconds) {String responseResult = jedisCluster.set(key,value);if(seconds!=0)jedisCluster.expire(key,seconds);return responseResult;}@Overridepublic String getSet(String key, String value, int seconds) {String jedisClusterSet = jedisCluster.getSet(key, value);jedisCluster.expire(key,seconds);return jedisClusterSet;}@Overridepublic String get(String key) {String str = jedisCluster.get(key);return str;}@Overridepublic Long geoadd(String key, double longitude, double latitude, byte[] obj) {return null;}@Overridepublic List<GeoRadiusResponse> georadius(String key, double longitude, double latitude) {return null;}@Overridepublic void delKey(String key) {jedisCluster.del(key);}@Overridepublic void delNativeKey(String key) {jedisCluster.del(key);}@Overridepublic Map<String, Object> getMapData(String key) {String str = jedisCluster.get(key);Map<String,Object> map = JSON.parseObject(str, Map.class);return map;}/*** @Description: 如为第一次,则加上锁,每次调用值会自动加1* @Param:* @return:* @Author:*/@Overridepublic boolean lock(String key, int seconds) {if(jedisCluster.incr(key)==1) {jedisCluster.expire(key,seconds);return false;}return true;}@Overridepublic void unlock(String key) {jedisCluster.del(key);}@Overridepublic String getLocakValue(String key) {return jedisCluster.get(key);}}

缓存键值:

package com.lagou.rocket.utils;public enum CacheKey {HASH_KEY("miaosha_v1_user_hash"),LIMIT_KEY("miaosha_v1_user_limit"),STOCK_COUNT("miaosha_v1_stock_count"),USER_HAS_ORDER("miaosha_v1_user_has_order");private String key;private CacheKey(String key) {this.key = key;}public String getKey() {return key;}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Curator实现dubbo服务自动注册发现

文章目录概念基于ServiceDiscovery实现服务自动注册和发现Service:服务基本信息InstanceDetails:封装实例用过来保存到zk中ServiceProvider&#xff1a;服务提供者ServiceConsumer&#xff1a;服务消费者运行基于ServiceDiscovery、ServiceCache实现服务自动注册和发现Registry…

jdk、cglib动态代理代码示例

文章目录jdk动态代理实现步骤代码示例新建一个接口新建一个接口的实现类新建一个代理类调用测试cglib动态代理实现实现步骤创建一个实现类新建一个代理类调用测试jdk动态代理 实现步骤 新建一个接口新建一个接口的实现类新建一个代理类&#xff0c;实现InvocationHandler接口…

Netty 客户端服务器端通信 demo

服务端 package com.demo.rpc.netty;import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketC…

Solr实战篇

1.在MySQL数据中建立lagou_db数据库, 将position.sql中的数据导入到mysql 数据中。 2.使用Solr的DIH 将mysql中的数据导入到Solr中。 3.使用SpringBoot 访问Solr 使用positionName 字段检索职位信息 如果检索到的职位信息不够5条 则需要 启用positionAdvantage 查找 美女多、…

Docker 部署java服务

作业描述&#xff1a; &#xff08;1&#xff09;Hot是应用程序(springboot)&#xff0c;打成jar包&#xff1a;docker-demo-1.0-SNAPSHOT.jar &#xff08;2&#xff09;利用dockerfile将docker-demo-1.0-SNAPSHOT.jar构建成镜像docker-demo Dockerfile-docker-demo&#xf…

单向链表 双向链表 java代码实现

文章目录单向链表代码实现单元测试控制台打印头插法尾插法双向链表代码实现单元测试控制台打印头插法尾插法单向链表 代码实现 package csdn.dreamzuora.list;/*** author: weijie* Date: 2020/10/15 15:28* Description:*/ public class SingleNode {int id;String name…

栈、队列 java代码实现

文章目录普通队列数组实现java代码实现单元测试控制台打印链表实现java代码实现单元测试控制台打印LinkedList队列使用优先队列&#xff1a;PriorityQueue使用栈数组实现java代码实现单元测试控制台打印链表实现java代码实现单元测试控制台打印普通队列 概念&#xff1a;先入先…

ElasticSearch入门篇

文章目录控制台RESTFULL操作REST风格说明基于REST命令说明&#xff1a;es支持的数据类型核心数据类型ik分词器使用ik_smart最少切分ik_max_word最细粒度索引操作索引库创建创建文档方式指定索引类型(以后这种方式会被弃用)不指定索引类型利用默认的_doc类型更新文档方式一&…

【待完成】并发包下常用到线程工具类汇总

文章目录并发容器BlockingQueueArrayBlockingQueueLinkedBlockingQueuePriorityBlockingDelayQueueSynchronousQueueBlockingDequeCopyOnWriteCopyOnWriteArrayListCopyOnWriteArraySetConcurrentLinkedQueue/DequeConcurrentHashMapConcurrentSkipListMap/Set同步工具类AQS实现…

爬虫中如何获取页面编码类型

获取页面的编码格式的三种方式&#xff1a; 根据Response中的header获取编码格式根据页面标签中的meta获取根据页面内容识别自动识别出编码格式&#xff0c;经过测试准确率比较高 三种方式可以结合使用&#xff0c;由于inputStream不能够被复用&#xff0c;但是inputStrem没有…

Spark集群部署与架构

在大数据时代&#xff0c;处理海量数据需要分布式计算框架。Apache Spark作为一种强大的大数据处理工具&#xff0c;可以在集群中高效运行&#xff0c;处理数十TB甚至PB级别的数据。本文将介绍如何构建和管理Spark集群&#xff0c;以满足大规模数据处理的需求。 Spark集群架构…

java核心技术-多线程并发设计原理以及常见面试题

文章目录写在前面多线程回顾Thread和Runnable面试官&#xff1a;为什么我们在项目中推荐使用使用Runnable方法而不用继承Thread&#xff1f;面试官&#xff1a;Callable为什么可以带返回值&#xff0c;你知道底层原理&#xff1f;面试题&#xff1a;线程了解&#xff1f;给我讲…

java核心技术-jvm基础知识

文章目录JVM回顾JVM、JRE、JDK之间关系&#xff1f;Java程序执行过程&#xff1f;面试官&#xff1a;解释执行和JIT(及时编译)两种执行方式有什么区别&#xff1f;java虚拟机内存管理jvm整体架构JVM只是定义内存划分规范等&#xff0c;具体实现依赖不同虚拟机实现&#xff0c;如…

多线程场景下利用ThreadLocal是线程安全?

文章目录背景多线程场景测试代码结论背景 ThreadLocal原理以及基本概念这里我就不介绍了&#xff0c;这里我们主要关注ThreadLocal是否是线程安全吗&#xff1f;其实如果我们知道ThreadLocal原理我们肯定知道它是线程安全的&#xff0c;但是我在开发的时候不放心做了个测试&am…

深入剖析线程池基本原理以及常见面试题详解

文章目录面试官&#xff1a;能给我讲讲线程池的实现原理&#xff1f;线程池类继承关系ThreadPoolExecutor核心数据结构面试官&#xff1a;给我讲讲线程池的有哪些参数&#xff1f;面试官&#xff1a;如何优雅的关闭线程&#xff1f;线程的生命周期面试官&#xff1a;线程池哪五…

设计模式七大设计原则

文章目录设计模式七大设计原则开闭原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则-最少知道原则单一职责原则合成复用原则设计模式 面向对象的三个基本特征&#xff1a; 继承封装多态 设计模式体现了代码的耦合性、内聚性、可维护性、可扩展性、重用性、灵活性。 代码…

从框架源码中学习结构型设计模式

文章目录从框架源码学习结构型设计模式适配器模式应用实例案例一&#xff1a;dubbo框架日志适配器Logger接口日志实现类Logger适配器接口LoggerAdapter实现类Logger日志工厂桥接模式应用场景案例&#xff1a;dubbo源码-远程调用模块channelHandler设计ChannelHandler是一个SPI拓…

MDC日志logback整合使用

MDC日志logback整合使用 为什么使用MDC记录日志&#xff1f; 场景&#xff1a; 由于我的搜索服务并发量比较高&#xff0c;而处理一次搜索请求需要记录多个日志&#xff0c;因此日志特别多的情况下去查一次搜索整个日志打印情况会比较复杂。 解决方案&#xff1a; 可以使用用…

如何合理的配置线程数?

文章目录题记Java并发编程实战美团技术团队追求参数设置合理性线程池参数动态化题记 我想不管是在面试中、还是工作中&#xff0c;我们总会面临这种问题&#xff0c;那么到底有没有一种计算公式去告诉我们如何去配置呢&#xff1f; 答案是&#xff1a;没有 想要合理的配置线…

基于CompletableFuture并发任务编排实现

文章目录并发任务编排实现不带返回值/参数传递任务串行执行并行执行并行执行-自定义线程池阻塞等待&#xff1a;多并行任务执行完再执行任意一个任务并发执行完就执行下个任务串并行任务依赖场景带返回值/参数传递任务带返回值实现串行执行多线程任务串行执行对任务并行执行&am…