zoukankan      html  css  js  c++  java
  • Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)

    转自:https://blog.csdn.net/qq_41305266/article/details/81146716

    一、思路:减少数据库访问

    1.系统初始化,把商品库存数量加载到Redis

    2.收到请求,Redis预减库存,库存不足,直接返回,否则进入3

    3.请求入队,立即返回排队中

    4.请求出队,生成订单,减少库存

    5.客户端轮询,是否秒杀成功

    二、安装RabbitMQ及其相关依赖

    下载erlang

    https://www.erlang.org/downloads

    下载rabbitMQ

    http://www.rabbitmq.com/download.html

    安装相关依赖

    yum install ncurses-devel

    tar xf otp_src_21.0.tar.gz

    cd otp_src_21.0

    ./configure --prefix=/usr/local/erlang21 --without-javac

    make -j 4

    make install

    验证安装是否成功

    yum install python -y

    yum install xmlto -y

    yum install python-simplejson -y

    xz -d rabbitmq-server-generic-unix-3.7.7.tar.xz

    tar xf rabbitmq-server-generic-unix-3.7.7.tar

    mv rabbitmq_server-3.7.7 /usr/local/rabbitmq

    vim /etc/profile

    在最后一行添加 export PATH=$PATH:/usr/local/erlang21/bin:/usr/local/rabbitmq/sbin

    source /etc/profile

    为了使guest用户让远程也可以访问,需要加入以下配置文件及内容

    vim /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config

    [{rabbit, [{loopback_users, []}]}].

    重启rabbitmq使其生效。

    三、SpringBoot集成RabbitMQ

    pom文件引入依赖

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    application.properties添加相关配置

    #rabbitmq
    spring.rabbitmq.host=120.78.235.152
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=guest
    spring.rabbitmq.password=guest
    spring.rabbitmq.virtual-host=/
    #u6D88u8D39u8005u6570u91CF
    spring.rabbitmq.listener.simple.concurrency= 10
    spring.rabbitmq.listener.simple.max-concurrency= 10
    #u6D88u8D39u8005u6BCFu6B21u4ECEu961Fu5217u83B7u53D6u7684u6D88u606Fu6570u91CF
    spring.rabbitmq.listener.simple.prefetch= 1
    #u6D88u8D39u8005u81EAu52A8u542Fu52A8
    spring.rabbitmq.listener.simple.auto-startup=true
    #u6D88u8D39u5931u8D25uFF0Cu81EAu52A8u91CDu65B0u5165u961F
    spring.rabbitmq.listener.simple.default-requeue-rejected= true
    #u542Fu7528u53D1u9001u91CDu8BD5
    spring.rabbitmq.template.retry.enabled=true
    spring.rabbitmq.template.retry.initial-interval=1000
    spring.rabbitmq.template.retry.max-attempts=3
    spring.rabbitmq.template.retry.max-interval=10000
    spring.rabbitmq.template.retry.multiplier=1.0
    添加配置类

    package com.wings.seckill.config;

    import org.springframework.amqp.core.Queue;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    public class MQConfig {

    public static final String QUEUE = "queue";

    @Bean
    public Queue queue(){
    return new Queue(QUEUE, true);
    }

    }
    发送者

    package com.wings.seckill.rabbitmq;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.amqp.core.AmqpTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.wings.seckill.config.MQConfig;
    import com.wings.seckill.redis.RedisService;

    @Service
    public class MQSender {

    private Logger logger = LoggerFactory.getLogger(MQSender.class);

    @Autowired
    private RedisService redisService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
    * Direct 模式交换机
    * @param obj
    */
    public void send(Object obj){
    String msg = redisService.beanToString(obj);
    logger.info("sender send:" + msg);
    amqpTemplate.convertAndSend(MQConfig.QUEUE, msg);
    }
    }
    接收者

    package com.wings.seckill.rabbitmq;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Service;

    import com.wings.seckill.config.MQConfig;

    @Service
    public class MQReceiver {

    private Logger logger = LoggerFactory.getLogger(MQReceiver.class);

    @RabbitListener(queues = MQConfig.QUEUE)
    public void receive(String msg){
    logger.info("receive:" + msg);
    }

    }
    DemoController添加以下测试方法

    @RequestMapping("/mq")
    @ResponseBody
    public Result<Boolean> mq() {
    mqSender.send("Wings you're the hero,路飞是成为海贼王的男人!");
    return Result.success(true);
    }
    结果如下

    2018-07-22 07:46:58.224  INFO 8624 --- [nio-8080-exec-6] com.wings.seckill.rabbitmq.MQSender      : sender send:Wings you're the hero,路飞是成为海贼王的男人!
    2018-07-22 07:46:58.235  INFO 8624 --- [cTaskExecutor-1] com.wings.seckill.rabbitmq.MQReceiver    : receive:Wings you're the hero,路飞是成为海贼王的男人!
     

    topic模式

    配置类添加以下配置方法


    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";
    public static final String TOPIC_EXCHANGE = "topicExchage";;


    @Bean
    public Queue topQueue1(){
    return new Queue(TOPIC_QUEUE1, true);
    }

    @Bean
    public Queue topQueue2(){
    return new Queue(TOPIC_QUEUE2, true);
    }

    @Bean
    public TopicExchange topicExchange(){
    return new TopicExchange(TOPIC_EXCHANGE);
    }

    @Bean
    public Binding topicBind1(){
    return BindingBuilder.bind(topQueue1()).to(topicExchange()).with("topic.key1");
    }

    @Bean
    public Binding topicBind2(){
    return BindingBuilder.bind(topQueue2()).to(topicExchange()).with("topic.#");
    }
    发送者添加以下方法

    public void sendTopic(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send topic message:" + msg);
    amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", msg + "1");
    amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", msg + "2");
    }
    接受者添加以下方法

    @RabbitListener(queues = MQConfig.TOPIC_QUEUE1)
    public void receiveTopic1(String msg){
    logger.info("receiveTopic1:" + msg);
    }

    @RabbitListener(queues = MQConfig.TOPIC_QUEUE2)
    public void receiveTopic2(String msg){
    logger.info("receiveTopic2:" + msg);
    }
    结果如下:

    fanout模式(广播模式)

    配置类添加以下配置方法

    @Bean
    public FanoutExchange fanoutExchange(){
    return new FanoutExchange(FANOUT_EXCHANGE);
    }

    @Bean
    public Binding fanoutBind1(){
    return BindingBuilder.bind(topQueue1()).to(fanoutExchange());
    }

    @Bean
    public Binding fanoutBind2(){
    return BindingBuilder.bind(topQueue2()).to(fanoutExchange());
    }
    发送者添加以下方法

    public void sendFanout(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send fanout message:" + msg);
    amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", msg);
    }
    结果如下:

    配置类添加以下配置方法

    @Bean
    public Queue headersQueue(){
    return new Queue(HEADERS_QUEUE, true);
    }

    @Bean
    public HeadersExchange headersExchange(){
    return new HeadersExchange(HEADERS_EXCHANGE);
    }

    @Bean
    public Binding headerBind(){
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("header1", "value1");
    map.put("header2", "value2");
    return BindingBuilder.bind(headersQueue()).to(headersExchange()).whereAll(map).match();
    }
     

    headers模式

    发送者添加以下方法

    public void sendHeaders(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send sendHeaders message:" + msg);

    MessageProperties props = new MessageProperties();
    props.setHeader("header1", "value1");
    props.setHeader("header2", "value2");
    Message obj = new Message(msg.getBytes(), props);
    amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", obj);
    }
    接收者添加以下方法

    @RabbitListener(queues = MQConfig.HEADERS_QUEUE)
    public void receiveHeaders(byte[] msg){
    logger.info("receiveHeaders:" + new String(msg));
    }
    结果如下:


    package com.wings.seckill.controller;

    import java.util.HashMap;
    import java.util.List;

    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;

    import com.wings.seckill.domain.SeckillOrder;
    import com.wings.seckill.domain.SeckillUser;
    import com.wings.seckill.rabbitmq.MQSender;
    import com.wings.seckill.rabbitmq.SeckillMessage;
    import com.wings.seckill.redis.GoodsKey;
    import com.wings.seckill.redis.OrderKey;
    import com.wings.seckill.redis.RedisService;
    import com.wings.seckill.redis.SeckillKey;
    import com.wings.seckill.result.CodeMsg;
    import com.wings.seckill.result.Result;
    import com.wings.seckill.service.GoodsService;
    import com.wings.seckill.service.OrderService;
    import com.wings.seckill.service.SeckillService;
    import com.wings.seckill.service.SeckillUserService;
    import com.wings.seckill.vo.GoodsVo;

    @Controller
    @RequestMapping("/seckill")
    public class SeckillController implements InitializingBean{

    @Autowired
    SeckillUserService userService;

    @Autowired
    RedisService redisService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    SeckillService seckillService;

    @Autowired
    MQSender sender;

    private HashMap<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();

    @Override
    public void afterPropertiesSet() throws Exception {
    List<GoodsVo> goodsList = goodsService.listGoodsVo();
    if(goodsList == null) {
    return;
    }
    for(GoodsVo goods : goodsList) {
    redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), goods.getStockCount());
    localOverMap.put(goods.getId(), false);
    }

    }

    @RequestMapping(value = "/do_seckill", method = RequestMethod.POST)
    @ResponseBody
    public Result<Integer> list(Model model, SeckillUser user, @RequestParam("goodsId") long goodsId) {
    model.addAttribute("user", user);
    if(user == null) {
    return Result.error(CodeMsg.SESSION_ERROR);
    }
    //内存标记,减少redis访问
    boolean over = localOverMap.get(goodsId);
    if(over) {
    return Result.error(CodeMsg.SECKill_OVER);
    }
    //预减库存
    long stock = redisService.decr(GoodsKey.getSeckillGoodsStock, ""+goodsId);
    if(stock < 0) {
    localOverMap.put(goodsId, true);
    return Result.error(CodeMsg.SECKill_OVER);
    }
    //判断是否已经秒杀到了
    SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(user.getId(), goodsId);
    if(order != null) {
    return Result.error(CodeMsg.REPEATE_SECKILL);
    }
    //入队
    SeckillMessage mm = new SeckillMessage();
    mm.setUser(user);
    mm.setGoodsId(goodsId);
    sender.sendSeckillMessage(mm);
    return Result.success(0);//排队中

    }

    @RequestMapping(value="/reset", method=RequestMethod.GET)
    @ResponseBody
    public Result<Boolean> reset(Model model) {
    List<GoodsVo> goodsList = goodsService.listGoodsVo();
    for(GoodsVo goods : goodsList) {
    goods.setStockCount(10);
    redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), 10);
    localOverMap.put(goods.getId(), false);
    }
    redisService.delete(OrderKey.getSeckillOrderByUidGid);
    redisService.delete(SeckillKey.isGoodsOver);
    seckillService.reset(goodsList);
    return Result.success(true);
    }

    /**
    * orderId:成功
    * -1:秒杀失败
    * 0: 排队中
    * */
    @RequestMapping(value="/result", method=RequestMethod.GET)
    @ResponseBody
    public Result<Long> seckillResult(Model model,SeckillUser user,
    @RequestParam("goodsId")long goodsId) {
    model.addAttribute("user", user);
    if(user == null) {
    return Result.error(CodeMsg.SESSION_ERROR);
    }
    long result =seckillService.getSeckillResult(user.getId(), goodsId);
    return Result.success(result);
    }
    }
    package com.wings.seckill.service;

    import java.util.List;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import com.wings.seckill.domain.OrderInfo;
    import com.wings.seckill.domain.SeckillOrder;
    import com.wings.seckill.domain.SeckillUser;
    import com.wings.seckill.redis.RedisService;
    import com.wings.seckill.redis.SeckillKey;
    import com.wings.seckill.vo.GoodsVo;

    @Service
    public class SeckillService {

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    RedisService redisService;

    @Transactional
    public OrderInfo seckill(SeckillUser user, GoodsVo goods) {
    //减库存 下订单 写入秒杀订单
    boolean success = goodsService.reduceStock(goods);
    if(success) {
    //order_info maiosha_order
    return orderService.createOrder(user, goods);
    }else {
    setGoodsOver(goods.getId());
    return null;
    }
    }

    public long getSeckillResult(Long userId, long goodsId) {
    SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(userId, goodsId);
    if(order != null) {//秒杀成功
    return order.getOrderId();
    }else {
    boolean isOver = getGoodsOver(goodsId);
    if(isOver) {
    return -1;
    }else {
    return 0;
    }
    }
    }

    private void setGoodsOver(Long goodsId) {
    redisService.set(SeckillKey.isGoodsOver, ""+goodsId, true);
    }

    private boolean getGoodsOver(long goodsId) {
    return redisService.exists(SeckillKey.isGoodsOver, ""+goodsId);
    }

    public void reset(List<GoodsVo> goodsList) {
    goodsService.resetStock(goodsList);
    orderService.deleteOrders();
    }
    }
    package com.wings.seckill.service;

    import java.util.List;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.wings.seckill.dao.GoodsDao;
    import com.wings.seckill.domain.SeckillGoods;
    import com.wings.seckill.vo.GoodsVo;

    @Service
    public class GoodsService {

    @Autowired
    GoodsDao goodsDao;

    public List<GoodsVo> listGoodsVo(){
    return goodsDao.listGoodsVo();
    }

    public GoodsVo getGoodsVoByGoodsId(long goodsId) {
    return goodsDao.getGoodsVoByGoodsId(goodsId);
    }

    public boolean reduceStock(GoodsVo goods) {
    SeckillGoods g = new SeckillGoods();
    g.setGoodsId(goods.getId());
    int ret = goodsDao.reduceStock(g);
    return ret > 0;
    }

    public void resetStock(List<GoodsVo> goodsList) {
    for(GoodsVo goods : goodsList ) {
    SeckillGoods g = new SeckillGoods();
    g.setGoodsId(goods.getId());
    g.setStockCount(goods.getStockCount());
    goodsDao.resetStock(g);
    }
    }



    }
    package com.wings.seckill.dao;

    import java.util.List;

    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    import org.apache.ibatis.annotations.Update;

    import com.wings.seckill.domain.SeckillGoods;
    import com.wings.seckill.vo.GoodsVo;

    @Mapper
    public interface GoodsDao {

    @Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.seckill_price from seckill_goods mg left join goods g on mg.goods_id = g.id")
    public List<GoodsVo> listGoodsVo();

    @Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.seckill_price from seckill_goods mg left join goods g on mg.goods_id = g.id where g.id = #{goodsId}")
    public GoodsVo getGoodsVoByGoodsId(@Param("goodsId")long goodsId);

    @Update("update seckill_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0")
    public int reduceStock(SeckillGoods g);

    @Update("update seckill_goods set stock_count = #{stockCount} where goods_id = #{goodsId}")
    public int resetStock(SeckillGoods g);

    }
    package com.wings.seckill.service;

    import java.util.Date;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import com.wings.seckill.dao.OrderDao;
    import com.wings.seckill.domain.OrderInfo;
    import com.wings.seckill.domain.SeckillOrder;
    import com.wings.seckill.domain.SeckillUser;
    import com.wings.seckill.redis.OrderKey;
    import com.wings.seckill.redis.RedisService;
    import com.wings.seckill.vo.GoodsVo;

    @Service
    public class OrderService {

    @Autowired
    OrderDao orderDao;

    @Autowired
    RedisService redisService;

    public SeckillOrder getSeckillOrderByUserIdGoodsId(long userId, long goodsId) {
    return redisService.get(OrderKey.getSeckillOrderByUidGid, ""+userId+"_"+goodsId, SeckillOrder.class);
    }

    public OrderInfo getOrderById(long orderId) {
    return orderDao.getOrderById(orderId);
    }

    @Transactional
    public OrderInfo createOrder(SeckillUser user, GoodsVo goods) {
    OrderInfo orderInfo = new OrderInfo();
    orderInfo.setCreateDate(new Date());
    orderInfo.setDeliveryAddrId(0L);
    orderInfo.setGoodsCount(1);
    orderInfo.setGoodsId(goods.getId());
    orderInfo.setGoodsName(goods.getGoodsName());
    orderInfo.setGoodsPrice(goods.getSeckillPrice());
    orderInfo.setOrderChannel(1);
    orderInfo.setStatus(0);
    orderInfo.setUserId(user.getId());
    orderDao.insert(orderInfo);
    SeckillOrder seckillOrder = new SeckillOrder();
    seckillOrder.setGoodsId(goods.getId());
    seckillOrder.setOrderId(orderInfo.getId());
    seckillOrder.setUserId(user.getId());
    orderDao.insertSeckillOrder(seckillOrder);

    redisService.set(OrderKey.getSeckillOrderByUidGid, ""+user.getId()+"_"+goods.getId(), seckillOrder);

    return orderInfo;
    }

    public void deleteOrders() {
    orderDao.deleteOrders();
    orderDao.deleteSeckillOrders();
    }

    }
    package com.wings.seckill.dao;

    import org.apache.ibatis.annotations.Delete;
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    import org.apache.ibatis.annotations.SelectKey;

    import com.wings.seckill.domain.OrderInfo;
    import com.wings.seckill.domain.SeckillOrder;

    @Mapper
    public interface OrderDao {

    @Select("select * from seckill_order where user_id=#{userId} and goods_id=#{goodsId}")
    public SeckillOrder getSeckillOrderByUserIdGoodsId(@Param("userId")long userId, @Param("goodsId")long goodsId);

    @Insert("insert into order_info(user_id, goods_id, goods_name, goods_count, goods_price, order_channel, status, create_date)values("
    + "#{userId}, #{goodsId}, #{goodsName}, #{goodsCount}, #{goodsPrice}, #{orderChannel},#{status},#{createDate} )")
    @SelectKey(keyColumn="id", keyProperty="id", resultType=long.class, before=false, statement="select last_insert_id()")
    public long insert(OrderInfo orderInfo);

    @Insert("insert into seckill_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})")
    public int insertSeckillOrder(SeckillOrder seckillOrder);

    @Select("select * from order_info where id = #{orderId}")
    public OrderInfo getOrderById(@Param("orderId") long orderId);

    @Delete("delete from order_info")
    public void deleteOrders();

    @Delete("delete from seckill_order")
    public void deleteSeckillOrders();


    }
    package com.wings.seckill.redis;

    import java.util.ArrayList;
    import java.util.List;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.alibaba.fastjson.JSON;

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.ScanParams;
    import redis.clients.jedis.ScanResult;

    @Service
    public class RedisService {

    @Autowired
    JedisPool jedisPool;

    /**
    * 获取当个对象
    * */
    public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    String str = jedis.get(realKey);
    T t = stringToBean(str, clazz);
    return t;
    }finally {
    returnToPool(jedis);
    }
    }

    /**
    * 设置对象
    * */
    public <T> boolean set(KeyPrefix prefix, String key, T value) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    String str = beanToString(value);
    if(str == null || str.length() <= 0) {
    return false;
    }
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    int seconds = prefix.expireSeconds();
    if(seconds <= 0) {
    jedis.set(realKey, str);
    }else {
    jedis.setex(realKey, seconds, str);
    }
    return true;
    }finally {
    returnToPool(jedis);
    }
    }

    /**
    * 判断key是否存在
    * */
    public <T> boolean exists(KeyPrefix prefix, String key) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    return jedis.exists(realKey);
    }finally {
    returnToPool(jedis);
    }
    }

    /**
    * 删除
    * */
    public boolean delete(KeyPrefix prefix, String key) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    long ret = jedis.del(realKey);
    return ret > 0;
    }finally {
    returnToPool(jedis);
    }
    }

    /**
    * 增加值
    * */
    public <T> Long incr(KeyPrefix prefix, String key) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    return jedis.incr(realKey);
    }finally {
    returnToPool(jedis);
    }
    }

    /**
    * 减少值
    * */
    public <T> Long decr(KeyPrefix prefix, String key) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    //生成真正的key
    String realKey = prefix.getPrefix() + key;
    return jedis.decr(realKey);
    }finally {
    returnToPool(jedis);
    }
    }

    public boolean delete(KeyPrefix prefix) {
    if(prefix == null) {
    return false;
    }
    List<String> keys = scanKeys(prefix.getPrefix());
    if(keys==null || keys.size() <= 0) {
    return true;
    }
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    jedis.del(keys.toArray(new String[0]));
    return true;
    } catch (final Exception e) {
    e.printStackTrace();
    return false;
    } finally {
    if(jedis != null) {
    jedis.close();
    }
    }
    }

    public List<String> scanKeys(String key) {
    Jedis jedis = null;
    try {
    jedis = jedisPool.getResource();
    List<String> keys = new ArrayList<String>();
    String cursor = "0";
    ScanParams sp = new ScanParams();
    sp.match("*"+key+"*");
    sp.count(100);
    do{
    ScanResult<String> ret = jedis.scan(cursor, sp);
    List<String> result = ret.getResult();
    if(result!=null && result.size() > 0){
    keys.addAll(result);
    }
    //再处理cursor
    cursor = ret.getStringCursor();
    }while(!cursor.equals("0"));
    return keys;
    } finally {
    if (jedis != null) {
    jedis.close();
    }
    }
    }

    public static <T> String beanToString(T value) {
    if(value == null) {
    return null;
    }
    Class<?> clazz = value.getClass();
    if(clazz == int.class || clazz == Integer.class) {
    return ""+value;
    }else if(clazz == String.class) {
    return (String)value;
    }else if(clazz == long.class || clazz == Long.class) {
    return ""+value;
    }else {
    return JSON.toJSONString(value);
    }
    }

    @SuppressWarnings("unchecked")
    public static <T> T stringToBean(String str, Class<T> clazz) {
    if(str == null || str.length() <= 0 || clazz == null) {
    return null;
    }
    if(clazz == int.class || clazz == Integer.class) {
    return (T)Integer.valueOf(str);
    }else if(clazz == String.class) {
    return (T)str;
    }else if(clazz == long.class || clazz == Long.class) {
    return (T)Long.valueOf(str);
    }else {
    return JSON.toJavaObject(JSON.parseObject(str), clazz);
    }
    }

    private void returnToPool(Jedis jedis) {
    if(jedis != null) {
    jedis.close();
    }
    }

    }
    package com.wings.seckill.rabbitmq;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.wings.seckill.config.MQConfig;
    import com.wings.seckill.domain.SeckillOrder;
    import com.wings.seckill.domain.SeckillUser;
    import com.wings.seckill.redis.RedisService;
    import com.wings.seckill.service.GoodsService;
    import com.wings.seckill.service.OrderService;
    import com.wings.seckill.service.SeckillService;
    import com.wings.seckill.vo.GoodsVo;

    @Service
    public class MQReceiver {

    private Logger logger = LoggerFactory.getLogger(MQReceiver.class);

    @Autowired
    RedisService redisService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    SeckillService seckillService;


    @RabbitListener(queues=MQConfig.SECKILL_QUEUE)
    public void receiveSeckill(String message) {
    logger.info("receive message:"+message);
    SeckillMessage mm = redisService.stringToBean(message, SeckillMessage.class);
    SeckillUser user = mm.getUser();
    long goodsId = mm.getGoodsId();

    GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
    int stock = goods.getStockCount();
    if(stock <= 0) {
    return;
    }
    //判断是否已经秒杀到了
    SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(user.getId(), goodsId);
    if(order != null) {
    return;
    }
    //减库存 下订单 写入秒杀订单
    seckillService.seckill(user, goods);
    }

    @RabbitListener(queues = MQConfig.QUEUE)
    public void receive(String msg){
    logger.info("receive:" + msg);
    }

    @RabbitListener(queues = MQConfig.TOPIC_QUEUE1)
    public void receiveTopic1(String msg){
    logger.info("receiveTopic1:" + msg);
    }

    @RabbitListener(queues = MQConfig.TOPIC_QUEUE2)
    public void receiveTopic2(String msg){
    logger.info("receiveTopic2:" + msg);
    }

    @RabbitListener(queues = MQConfig.HEADERS_QUEUE)
    public void receiveHeaders(byte[] msg){
    logger.info("receiveHeaders:" + new String(msg));
    }

    }
    package com.wings.seckill.rabbitmq;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.amqp.core.AmqpTemplate;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.core.MessageProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.wings.seckill.config.MQConfig;
    import com.wings.seckill.redis.RedisService;

    @Service
    public class MQSender {

    private Logger logger = LoggerFactory.getLogger(MQSender.class);

    @Autowired
    private RedisService redisService;

    @Autowired
    private AmqpTemplate amqpTemplate;


    public void sendSeckillMessage(SeckillMessage mm) {
    String msg = redisService.beanToString(mm);
    logger.info("send message:"+msg);
    amqpTemplate.convertAndSend(MQConfig.SECKILL_QUEUE, msg);
    }

    /**
    * Direct 模式交换机
    *
    * @param obj
    */
    public void send(Object obj) {
    String msg = redisService.beanToString(obj);
    logger.info("sender send:" + msg);
    amqpTemplate.convertAndSend(MQConfig.QUEUE, msg);
    }

    public void sendTopic(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send topic message:" + msg);
    amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", msg + "1");
    amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", msg + "2");
    }

    public void sendFanout(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send fanout message:" + msg);
    amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", msg);
    }

    public void sendHeaders(Object message) {
    String msg = redisService.beanToString(message);
    logger.info("send sendHeaders message:" + msg);

    MessageProperties props = new MessageProperties();
    props.setHeader("header1", "value1");
    props.setHeader("header2", "value2");
    Message obj = new Message(msg.getBytes(), props);
    amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", obj);
    }
    }
    <!DOCTYPE HTML>
    <html >
    <head>
    <title>商品详情</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <!-- jquery -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <!-- layer -->
    <script type="text/javascript" src="/layer/layer.js"></script>
    <!-- md5.js -->
    <script type="text/javascript" src="/js/md5.min.js"></script>
    <!-- common.js -->
    <script type="text/javascript" src="/js/common.js"></script>
    </head>
    <body>

    <div class="panel panel-default" >
    <div class="panel-heading">秒杀商品详情</div>
    <div class="panel-body">
    <span id="userTip"> 您还没有登录,请登陆后再操作<br/></span>
    <span>没有收货地址的提示。。。</span>
    </div>
    <table class="table" id="goodslist">
    <tr>
    <td>商品名称</td>
    <td colspan="3" id="goodsName"></td>
    </tr>
    <tr>
    <td>商品图片</td>
    <td colspan="3"><img id="goodsImg" width="200" height="200" /></td>
    </tr>
    <tr>
    <td>秒杀开始时间</td>
    <td id="startTime"></td>
    <td >
    <input type="hidden" id="remainSeconds" />
    <span id="seckillTip"></span>
    </td>
    <td>
    <!--
    <form id="seckillForm" method="post" action="/seckill/do_seckill">
    <button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
    <input type="hidden" name="goodsId" id="goodsId" />
    </form>-->
    <button class="btn btn-primary btn-block" type="button" id="buyButton"onclick="doSeckill()">立即秒杀</button>
    <input type="hidden" name="goodsId" id="goodsId" />
    </td>
    </tr>
    <tr>
    <td>商品原价</td>
    <td colspan="3" id="goodsPrice"></td>
    </tr>
    <tr>
    <td>秒杀价</td>
    <td colspan="3" id="seckillPrice"></td>
    </tr>
    <tr>
    <td>库存数量</td>
    <td colspan="3" id="stockCount"></td>
    </tr>
    </table>
    </div>
    </body>
    <script>

    function getSeckillResult(goodsId){
    g_showLoading();
    $.ajax({
    url:"/seckill/result",
    type:"GET",
    data:{
    goodsId:$("#goodsId").val(),
    },
    success:function(data){
    if(data.code == 0){
    var result = data.data;
    if(result < 0){
    layer.msg("对不起,秒杀失败");
    }else if(result == 0){//继续轮询
    setTimeout(function(){
    getSeckillResult(goodsId);
    }, 50);
    }else{
    layer.confirm("恭喜你,秒杀成功!查看订单?", {btn:["确定","取消"]},
    function(){
    window.location.href="/order_detail.htm?orderId="+result;
    },
    function(){
    layer.closeAll();
    });
    }
    }else{
    layer.msg(data.msg);
    }
    },
    error:function(){
    layer.msg("客户端请求有误");
    }
    });
    }

    function doSeckill(){
    $.ajax({
    url:"/seckill/do_seckill",
    type:"POST",
    data:{
    goodsId:$("#goodsId").val(),
    },
    success:function(data){
    if(data.code == 0){
    //window.location.href="/order_detail.htm?orderId="+data.data.id;
    getSeckillResult($("#goodsId").val());
    }else{
    layer.msg(data.msg);
    }
    },
    error:function(){
    layer.msg("客户端请求有误");
    }
    });

    }

    function render(detail){
    var seckillStatus = detail.seckillStatus;
    var remainSeconds = detail.remainSeconds;
    var goods = detail.goods;
    var user = detail.user;
    if(user){
    $("#userTip").hide();
    }
    $("#goodsName").text(goods.goodsName);
    $("#goodsImg").attr("src", goods.goodsImg);
    $("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
    $("#remainSeconds").val(remainSeconds);
    $("#goodsId").val(goods.id);
    $("#goodsPrice").text(goods.goodsPrice);
    $("#seckillPrice").text(goods.seckillPrice);
    $("#stockCount").text(goods.stockCount);
    countDown();
    }

    $(function(){
    //countDown();
    getDetail();
    });

    function getDetail(){
    var goodsId = g_getQueryString("goodsId");
    $.ajax({
    url:"/goods/detail/"+goodsId,
    type:"GET",
    success:function(data){
    if(data.code == 0){
    render(data.data);
    }else{
    layer.msg(data.msg);
    }
    },
    error:function(){
    layer.msg("客户端请求有误");
    }
    });
    }

    function countDown(){
    var remainSeconds = $("#remainSeconds").val();
    var timeout;
    if(remainSeconds > 0){//秒杀还没开始,倒计时
    $("#buyButton").attr("disabled", true);
    $("#seckillTip").html("秒杀倒计时:"+remainSeconds+"秒");
    timeout = setTimeout(function(){
    $("#countDown").text(remainSeconds - 1);
    $("#remainSeconds").val(remainSeconds - 1);
    countDown();
    },1000);
    }else if(remainSeconds == 0){//秒杀进行中
    $("#buyButton").attr("disabled", false);
    if(timeout){
    clearTimeout(timeout);
    }
    $("#seckillTip").html("秒杀进行中");
    }else{//秒杀已经结束
    $("#buyButton").attr("disabled", true);
    $("#seckillTip").html("秒杀已经结束");
    }
    }

    </script>
    </html>
    最终优化效果如下:


    比原来提高了3倍QPS!

  • 相关阅读:
    (原)Lazarus 异构平台下多层架构思路、DataSet转换核心代码
    (学)新版动态表单研发,阶段成果3
    (学) 如何将 Oracle 序列 重置 清零 How to reset an Oracle sequence
    (学)XtraReport WebService Print 报错
    (原)三星 i6410 刷机 短信 无法 保存 解决 办法
    (原) Devexpress 汉化包 制作工具、测试程序
    linux下网络配置
    apache自带ab.exe小工具使用小结
    Yii::app()用法小结
    PDO使用小结
  • 原文地址:https://www.cnblogs.com/sharpest/p/10960411.html
Copyright © 2011-2022 走看看