暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

线上问题的对话

胡钦振 2021-09-18
217

PS:老王看最近任务稍微少些,老王作为老大,想着锻炼下其它经验稍欠缺的小伙伴,就准备分下去,一来给点压力,二来自己也可以歇一歇,偷个懒,整理下下个月的计划和任务。(聊天案例部分来源于OSCHAINA,博客园)

老王:最近项目线上有几个问题,可能是服务器kafka消息发送失败引起的,要不你上去查下看看?
小张:(内心慌的一批)好,我先查查生产日志看看,确定是kafka生产没发消息还是啥原因吧。
......
经过小张查找一番以后,确实是到发送kafka消息时,日志没打,于是小张查下常用命令,要让运维小哥帮忙查查。运维小哥一阵霹雳啪啦开始操作;

kafka查询命令


  1. kafka版本:kafka_2.12-2.1.1(以前版本可能不支持)


  2. kafka_port默认9092,zk_port默认2181


  3. 查看topic bin/kafka-topics.sh --zookeeper zk_ip:zk_port --list


  4. 查看历史数据: ./kafka-console-consumer.sh --bootstrap-server 192.168.101.223:9092 --topic MACTIVITY_WARE --from-beginning |grep '13752592881 '


  5. 查看实时日志: ./kafka-console-consumer.sh --bootstrap-server 192.168.101.223:9092 --topic LOGMS_CALL_10 |grep '192.168.101.223'


  6. 查看topic下group的消费情况 bin/kafka-consumer-groups.sh --bootstrap-server kafka_ip:kafka_port --group group_name --describe


  7. 查看group bin/kafka-consumer-groups.sh --bootstrap-server kafka_ip:kafka_port --list

kafka有何优势?


某天,小张在电脑前,刷刷GitHub,正好刷到kafka,老王刚好看到,就给小张提了个问题;kafka相比较其它中间件rocketMQ,activeMQ有啥优势呢
小张:(果然不愧是老大)刚好上次看到过kafka相比较rocketMQ的区别,正好可以吹吹,是这样的,kafka是比较高性能的,高可用的消息队列,具体说来kafka通过它的集群,分区保证它的高可用,稳定性能,具体说来就是:

  • 1. kafka的分区结构,是分布式存储的,具体说来就是kafka的消息是分布式存储在不同机器上,集群的形式,而且分区是在broker下面有多个副本机制的,避免因为单点故障造成的损失,broker下面的分区是有主从的,读取消费消息都是在leader分区,follower分区只是同步leader分区的消息,生产者发消息到broker下面的leader分区,消费者也是消费broker下面的leader分区,副本机制用来防止单点故障。


  • 2. 分区的副本机制意味着多个线程来处理,性能优于单线程。


  • 3. kafka数据是存在硬盘的,不是存在内存的,有人说这样性能不是差很远么,你看redis这样多块啊,其实kafka是存在硬盘的,但是它是顺序写,这样它的速度其实跟内存相差无几。


重复消费?


老大:接着上一个问题,kafka使用过程出现重复消费么?
小张:(果然老大有备而来)额,这个嘛,还是有的,比如这个场景:用户在某平台下单,支付完了之后发消息到下游系统,积分系统,库存系统,优惠券系统,各个子系统按照消息来处理,假设在某个时候,积分系统处理异常了,没有成功消费到,它想订单系统重新发消息,二次处理,这样是合理的,但是。。。订单系统发的消息,只有积分系统收到嘛,显然不是,交易系统其它的都收到了,那这样不就出事了么。假如没有处理重复消费的问题,那付了一次款,多领了20积分,交易流水多了1次,多扣了一次钱,那不就麻烦了(用户心里不知啥滋味呢)。
老大:那怎么解决呢?
小张:办法是有的,这就要求下游子系统做好消息的重复消费处理,这就是大家说到的接口幂等性了,一次或多次调用,返回相同的响应。比如一个订单,只扣一次款,扣一次库存,其结果如图:



老大:那具体的幂等性接口处理有哪几种呢
小张:常用的比如redis的token机制,数据库的乐观锁/悲观锁,给数据加唯一索引等,给数据加索引是比较简单的实现了,加唯一索引,是在终点来反馈,可以在代码里try-catch然后返回处理。

如何保证顺序消费?


老大:看来还是有点东西,那还有个问题,既然重复消费可以解决了,那顺序消费怎么解决呢
小张:.......
老大:老大看见我比较懵逼,然后举了个例子,比如下完订单,扣钱,系统发两个消息,怎么保证能够顺序发送,并且在消费端能够顺序消费呢
小张:(...这个还真有点难哦,望着天花板2-3秒,托腮)是不是能让消息队列控制发送顺序消费,比如rocketMQ,下完订单消息发送到队列后,选择同步发送,然后再发送支付消息,这是rocketMQ基于本身的topic的队列数据结构实现的,嗯,至于怎么顺序消费的,那就看消费端自己处理的了,这个应该也能做到。具体的就没仔细看了。
老大:也大致的理解了,好吧,那分布式事务你了解不,来聊聊看?小张:嗯,好,分布式事务也是分布式环境下会出现的一个问题,而且实际解决起来也比较麻烦,具体说来,有几种实现方式:

    1. 1. 2pc(两段式)

    2. 2. 3pc(三段式)

    3. 3. TCC(Try,Confirm,Cancel)

    4. 4.尽最大努力通知

    5. 5. 最终一致性

2段式时序图:



(实际上我也没遇到过这种情况,这是主流的分布式事务的几种解决方式~)

这种解决方式要求2个子系统事务都要执行成功,如果A成功提交了,B提交失败了,还是当作失败来处理,如果是B自身的事务执行失败倒也没啥,如果是网络延时,或者其它问题呢,那就有问题了, 两段式是比较早的解决方案,在并发环境下,可能会出现响应速度极慢,效率不是很高。

老大:还不错,那最终一致性是怎么处理的呢?
小张:这个,,额,下次再说吧,今天天色已晚,不如隔日再聊?
老大:好吧。

重复消费处理示例:

//kafka防止重复消费,借助redis
public void exists(String key){
String LOCK_KEY ="lock_key"+key;
boolean flag =false;
String val = redisHelper.getSet(LOCK_KEY,"1");
if(StringUtils.isNotEmpty(val)){
flag =true;
}
return flag;
}

//业务逻辑
boolean repeatFlag =false;
String key= getUserKey(userId);
repeatFlag = exists(key);
try{
if(repeatFlag){
return;
}else{
flag = true;
//后续处理
}
}catch(Exception e){
//不做处理
}finally{
if(flag){
//删掉key
redisHelper.del(key)
}
}

文章转载自胡钦振,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论