结局:总结+分享
看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。
开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱
- Java互联网工程师面试1000题
而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。
- 程序员代码面试指南–IT名企算法与数据结构题目最优解
- 其余像设计模式,建议可以看看下面这4份PDF(已经整理)
- 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。
以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!
-
at least once
→ 至少一次,消息肯定不会丢失,但可能重复 -
exactly once
→ 有且只有一次,消息不丢失不重复,且只消费一次。
exactly once
尽可能是 producer + consumer 两端都保证。当 producer 没办法保证是,那 consumer 需要在消费前做一个去重,达到消费过一次不会重复消费,这个在延迟队列内部直接保证。
最简单:使用 redis 的 setNX 达到 job id 的唯一消费
高可用
支持多实例部署。挂掉一个实例后,还有后备实例继续提供服务。
这个对外提供的 API 使用 cluster 模型,内部将多个 node 封装起来,多个 node 之间冗余存储。
为什么不使用 kafka?
考虑过类似基于 kafka/rocketmq 等消息队列作为存储的方案,最后从存储设计模型放弃了这类选择。
举个例子,假设以 Kafka 这种消息队列存储来实现延时功能,每个队列的时间都需要创建一个单独的 topic(如: Q1-1s, Q1-2s…)。这种设计在延时时间比较固定的场景下问题不太大,但如果是延时时间变化比较大会导致 topic 数目过多,会把磁盘从顺序读写会变成随机读写从导致性能衰减,同时也会带来其他类似重启或者恢复时间过长的问题。
-
topic 过多 → 存储压力
-
topic 存储的是现实时间,在调度时对不同时间 (topic) 的读取,顺序读 → 随机读
-
同理,写入的时候顺序写 → 随机写
架构设计
API 设计
producer
-
producer.At(msg []byte, at time.Time)
-
producer.Delay(body []byte, delay time.Duration)
-
producer.Revoke(ids string)
consumer
consumer.Consume(consume handler)
使用延时队列后,服务整体结构如下,以及队列中 job 的状态变迁:
-
service →
producer.At(msg []byte, at time.Time)
→ 插入延时job到 tube 中 -
定时触发 → job 状态更新为 ready
-
consumer 获取到 ready job → 取出 job,开始消费;并更改状态为 reserved
-
执行传入 consumer 中的 handler 逻辑处理函数
生产实践
主要介绍一下在日常开发,我们使用到延时队列的哪些具体功能。
生产端
-
开发中生产延时任务,只需确定任务执行时间
-
传入 At()
producer.At(msg []byte, at time.Time)
-
内部会自行计算时间差值,插入 tube
-
如果出现任务时间的修改,以及任务内容的修改
-
在生产时可能需要额外建立一个 logic_id → job_id 的关系表
-
查询到 job_id →
producer.Revoke(ids string)
,对其删除,然后重新插入
消费端
首先,框架层面保证了消费行为的 exactly once
,但是上层业务逻辑消费失败或者是出现网络问题,亦或者是各种各样的问题,导致消费失败,兜底交给业务开发做。这样做的原因:
-
框架以及基础组件只保证 job 状态的流转正确性
-
框架消费端只保证消费行为的统一
-
延时任务在不同业务中行为不统一
-
强调任务的必达性,则消费失败时需要不断重试直到任务成功
-
强调任务的准时性,则消费失败时,对业务不敏感则可以选择丢弃
这里描述一下框架消费端是怎么保证消费行为的统一:
分为 cluster 和 node。cluster:
https://github.com/tal-tech/go-queue/blob/master/dq/consumer.go#L45
-
cluster 内部将 consume handler 做了一层再封装
-
对 consume body 做hash,并使用此 hash 作为 redis 去重的key
-
如果存在,则不做处理,丢弃
node:
go-queue/consumernode.go at master · tal-tech/go-queue · GitHub
最后
为什么我不完全主张自学?
①平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。
除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。
我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。
应该学哪些技术才能达到企业的要求?(下图总结)
9948274)]