从购物车设计引发的一系列问题(rocketMQ在虚拟机中启动注意事项)

  购物车大家都习惯用过,添加购物车,删除购物车等等操作。

  看了很多github或者码云都仓库代码,购物车的实现一般都是直接操作数据库,进行增删改查。

  个人认为是不对的!

  你可以说,数据库可以分库分表或者其他操作。但是对于频繁操作数据库的,会造成数据库io崩掉,然后直接导致系统挂掉。这就是为什么很多数据刚开始会用缓存处理的原因之一。

  最后,数据肯定是要沉淀到数据库中去的,但是我们不想过多的浪费业务逻辑去操作数据库,这就可以采用异步方式,这里我使用了rocketmq去实现。

  下面是一个简单实现(业务逻辑也相对简单,没有sql处理,看注释即可)

  

<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.2.0</version>
</dependency>

记得加上这个。

@RestController
@RequestMapping("/cart")
public class CartController {

@Autowired
private CartService cartService;


/**
*
* @return
*/
@PostMapping("")
public Cart addCart(@RequestBody Cart cart, String userId) {
return cartService.addCart(cart, userId);
}

}

rest中有一个添加cart的接口。
我们看下service中实现


@Slf4j
@Service
public class CartService {


@Autowired
private RedisTemplate<String,String> redisTemplate;


@Autowired
private Producer producer;
/**
* 添加商品到购物车
* @param cart
* @return
*/
public Cart addCart(Cart cart, String userId) {
//并发量大的话 其实不要连mysql
//添加购物车 就是直接往redis中去添加
//直接操作redis 所以不用注解Cache
//把信息带到MQ 服务从MQ 中去消费 添加到数据库 才行
//查询是否有相关productid
//用hash去存储
Object hash = redisTemplate.opsForHash().get("cart:" + userId,"cart:" + cart.getProductId());
if(hash != null) {
Integer newNum = Integer.parseInt(hash.toString()) + cart.getNum();
redisTemplate.opsForHash().put("cart:" + userId,"cart:" + cart.getProductId(), newNum.toString());
} else {
redisTemplate.opsForHash().put("cart:" + userId,"cart:" + cart.getProductId(), cart.getNum().toString());
}

//异步传入到消息
try{
Message message = new Message(RocketConfig.CART_TOPIC, JSON.toJSONBytes(cart));
SendResult result = producer.getProducer().send(message);
log.info("生产者传入消息到消费者---{}", result);
}catch (Exception e) {
log.error(e.getMessage());
}
return cart;
}

}


@Data
public class Cart {

private String id;

private String shopId;

private String userId;

private String productId;

private Integer num;
}


public class RocketConfig {

public static final String NAMESRV = "192.168.100.108:9876";

public static final String CART_TOPIC = "cart_topic";
}

@Slf4j
@Component
public class Producer {

private String proupName = "cart_group";


private DefaultMQProducer producer;

public Producer() {
producer = new DefaultMQProducer(proupName);
producer.setNamesrvAddr(RocketConfig.NAMESRV);
producer.setVipChannelEnabled(false);
start();
}

public void start() {
try {
this.producer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
}

public void shutdown() {
this.producer.shutdown();
}

public DefaultMQProducer getProducer() {
return this.producer;
}
}

@Slf4j
@Component
public class Cousmer {

private DefaultMQPushConsumer consumer;

private String groupName = "cart_group";


public Cousmer() throws MQClientException {

consumer = new DefaultMQPushConsumer(groupName);
consumer.setNamesrvAddr(RocketConfig.NAMESRV);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
consumer.subscribe(RocketConfig.CART_TOPIC, "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list
, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt message : list) {
try {
String body = new String(message.getBody(), "utf-8");
log.info("Consumer-获取消息-主题topic为={}, 消费消息为={}", message.getTopic(), body);
//添加数据到数据库中逻辑
log.info("添加到数据库中" + message.getMsgId());
} catch (UnsupportedEncodingException e) {
//e.printStackTrace();
log.error("消费者异常");
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});

consumer.start();
System.out.println("消费者 启动成功=======");
}
}

ok 这是几个处理类

首先说下我自己遇到的问题
1、在设计过程中,购物车在redis中的存储结构应该是hash

可以看到,这就是用户id为22的这个用户所添加的购物车以及数量,qw12313为产品id。

可能业务代码写的比较凌乱,但是基本思想都差不多。

其次,我的redis以及mq 都是搭建在虚拟机中。

 

ok,那么问题来了,redis访问没问题,一直都可以。

但是mq都访问遇到坑了,要不就是route not info......要不就是脸上namesrv但是消息发送失败!

这里比较好多方式是

rocketmq-console,大家部署这个,然后看效果就知道了。

具体看每一个topic的路由,你会发现并不是你的虚拟机中ens33的网络,而是docker0的网络!

其实在官网,rocketmq是有介绍的,大家仔细一点可以发现,我们需要在broker.conf中去配置ip

 

 这样,访问就没问题。

看到没有!!

请求时间,测试了一下,在100ms以内(大家设计接口时候,最后接口访问返回时间控制在200ms以内,这样才是优秀的接口!)

额。。。写的并不是很完善,包括rocketmq-console的部署也没说,其实网上一大堆,大家都可以参考都用!

如果上述你都redis,mq都是在本地启动都话,其实没必要了,访问是成功的,我这里因为在虚拟机中搭建比较坑,所以大家还是仁者见仁,智者见智!

猜你喜欢

转载自www.cnblogs.com/lzphu/p/12571454.html