极客社区项目技术点整理
本文最后更新于274 天前,其中的信息可能已经过时,如有错误请发送邮件到15578243672@163.com

此文章为了之后方便复习项目中的有关技术点而设计,会随着面试过程的推进更新(回答统一为业务流程是什么,用了什么技术,为什么使用这些技术)

1.通过 验证码与前端验证码与前端保持半长链接映射关系保持半长链接映射关系,当用户扫码关注公众号并输入验证码后,系统发起回调,识别用户信息并匹配半长链接,实现自动登录。

关于这个微信扫码登录的业务流程是这样的,当前端用户选择微信公众号登录的时候,后端会返回给用户端二维码以及验证码,这个时候会建立起一个设备ID与验证码的映射关系(此处为业务逻辑迭代导致,本意想用户自己再次输入返回的验证码进行登录,后续改成用户只需要在公众号输入验证码就可以自行登录),以及通过sse技术建立一个单工通信的长时连接并且建立一个与验证码的映射关系记入本地缓存中并设计过期时间防止(?),不使用webSocket的原因是sse更加轻量,而且这个场景是一个单向通信的场景。为了相应微信的回调机制,我们需要设计一个专门用于回应微信返回相应的接口,用于接收用户在微信端与公众号进行的交互信息,这就是微信的回调机制,微信以http请求返回用户在公众号输入的验证码之后,后端根据验证码查询相关的sse连接,就可以使用自动登录功能,用户无需额外的操作

衍生问题:

你所说的设备ID是什么?

其实就是Session Identifier,设备Id的获取有三层,第一层是从请求的deviceId中获取,第二层是获取cookie中名为为f-device的Cookie,如果这两个都没有, 我会生成一个UUID用作设备Id,并将其摄入cookie中以便下次使用,也就是当用户刷新的时候或者关闭浏览器之后,只要没过期,我的后端依然能识别出是同一个设备。当刷新验证码的时候也会重新建立一个设备与验证码的映射关系

你说的sse过期了怎么办,超时了怎么办,怎么清理缓存的

我考虑了这些情况,因为缓存中设置了自动过期,当sse过期的时候缓存也就删除了,不会存在僵尸缓存,这个时候用户向微信公众号发送验证码,发起回调之后找不到验证码与sse的映射关系,就会提示用户超时或者网络波动,重新刷新获取新的验证码。

关于本地缓存的清理?

我在缓存设置了两个维度的限制,一个是时间维度的,当时间超时,都会清除本地缓存中的记录,第二个我是从空间维度进行设置,我设置了300条数据的限制,当数据量超过300时,就会采用LRU算法进行清除。最后就是自动清除机制,当用户登录成功的时候就会自动清除在本地缓存中的记录,或者因为网络波动时,当能刷新验证码时,我都会清除缓存中的记录

关于微信回调方面?

关于验证回调确实来自于微信,我们有两个阶段,第一阶段是微信发起回调需要在微信平台填写好我们准备好的url地址,在首次配备的时候,微信会向我们发起一个get请求,包含参数签名,时间戳,随机字符串,随机数,我们同样也用使用相同的算法来使用返回的随机数和时间戳来计算签名进行比对,如果相同的话就把随机字符串返回给微信平台,这是第一步验证
之后的Post请求除了数据体之外,还有签名,之后我们也要通过时间戳和随机数来进行签名的重新计算和比对
关于信息交换方面,微信和我们的信息交换是一个xml的数据包,里面就包含着用户的唯一表示openId和其它的交互内容,在验证登录这个部分我会使用配置好的xml解析器(Jackson XML),封装到我定义好的Java对象中,之后关注content中的内容,这部分携带了验证码,进入登录验证流程,处理完毕后,我们的系统也需要按照微信要求的XML格式构造响应消息,通过同样的XML结构返回给微信服务器,微信再将这个回复展示给用户

登录成功后,您如何管理用户状态?下发JWT还是Session?
这个项目是在登录完成之后为用户生成一个SeesionId放在cookie中,之后的请求就会从cookie中拿取用户的信息,当然我们也可以使用JWT前面,只需要返回给客户端一个token,之后的请求都携带这个token就可以,这个返回的通道就是我之前提到的sse连接(统中预定义了一个专门的Session键名,叫做”f-session”。这意味着所有用户的会话信息都会以这个名称作为标识存储在Cookie中。
当用户后续访问网站的其他页面时,浏览器会自动在每个HTTP请求的Cookie头中携带这个”f-session”信息。服务器端就可以通过读取这个Cookie来识别用户的身份)

2.将 用户评论、点赞、收藏、系统消息用户评论、点赞、收藏、系统消息 等操作发送至 RabbitMQ,实现消息的 异步解耦且还采用 Redisson 看门狗策略异步解耦,并Redisson 看门狗策略优化缓存架构,针对热 key 的并发访问进行同步,防止其失效时导致的缓存击穿问题。

这个项目在处理用户交互行为时,采用了”核心操作同步执行,辅助操作异步处理”的设计理念。当用户执行评论、点赞、收藏等操作时,系统会将这些操作分为两个部分:
第一部分是核心业务逻辑,比如保存评论内容到数据库、更新点赞计数、记录收藏关系等,这些操作需要立即完成,确保数据的一致性,所以是同步执行的。
第二部分是辅助性的通知和统计工作,比如发送系统通知给相关用户、更新用户积分、记录操作日志等,这些操作不需要用户立即看到结果,可以通过异步方式处理,这就需要用到RabbitMQ消息队列。生产者依靠Publisher Confirm机制将这些消息推送到消息队列中,同时我的消费模式是工作队列模式,多个消费者同时监听一个消费队列,在消息体中我设计了一个字段来表明消息的类型,这样就是是统一的消费者消费,也会根据消息内容的不同采用不同的业务操作。为了保证不重复消费,我采用了数据库的唯一性检测和维护一个任务状态表,当同一个消息被发完不同的消息队列时,完成处理之后会插入数据库中,如果插入失败说明已经被处理,此时就可以不理会这个消息,对于消息被重复发往同一个消费者,我采用redis’记录任务id和状态的映射,在接收到新任务的时候,如果能在redis中查到数据的话,我们就直接抛弃这个消息,为了保证消费的可靠性我采用了手动ACK的机制,只有消费者向消息队列发送确认消息的时候才会删除消息。针对热点数据我也采用了redisson的看门狗机制来进行优化,也就是缓存经常说的缓存击穿问题,我采用了加锁机制保证只有一个线程对DB进行访问,防止DB压力过大,但出现了一个问题就是锁什么时候释放的问题,为了自动释放,我利用了redisson的看门狗机制来自动延长锁的时间。对于缓存雪崩问题,我采用设置随机过期时间来解决,防止缓存大面积失效,对于缓存穿透,对于不存在redis中设置了null,并设置了过期时间,防止之后数据库中真的存在这个数据

消费者是怎么组织的?如何保证不被重复消费,生产者是怎么发送消息的?消费可靠性的保证呢?

我的消费者是工作队列模式,也就是多个消费者共享一个消息队列中的内容,我的系统中有多个消息队列,当用户评论点赞的时候,我会把这些消息按照生成的顺序发到消费队列中,然后多个消费者实例同时监听这个队列,当有新消息到达的时候,队列采用轮询的方式将消息发给不同的消费者实例。虽然我是统一的消费者消费,但是消费者内容会根据消息内容的不同进行不同的业务操作。
为了确保生产者发送的消息能够成功到达RabbitMQ Broker,系统应该启用了Publisher Confirm机制。
当生产者发送消息到RabbitMQ时,不会立即认为消息发送成功,而是等待RabbitMQ的确认回复。只有收到Broker的确认后,生产者才会认为消息已经成功投递。如果在一定时间内没有收到确认,或者收到失败的确认,生产者会进行重试或者记录错误日志。
为了防止重复消费的问题,我采用了数据库的唯一性检查,但消费者拿到一条新的消息之后,会尝试往数据库中插入数据,如果不能插入数据,消费者就知道这条数据已经被处理过了,直接忽略,同时对同一个消费者发送相同的消息,我会记录任务的唯一ID和任务状态,之后获取到新的消息,就会先查状态表,如果已经存在则忽视这个消息。为了保证消息的正确处理,我采用了手动ACK的方式,但消息被发送给消费者是,这个消息会被标记为未确认状态,只有消费者处理完成之后并向消息队列发送确认消息之后才会删除里面的内容,如果消费者宕机的话,因为没有发送ACK,消息依然存在在消息队列中

你的消费者是怎么按照对应消息采取对应的业务操作的

3.使用 HandlerExceptionResolver 全局异常处理策略
HandlerExceptionResolver 全局异常处理策略,提高代码健壮性与可维护性,优化用户体验

是的,我引入这个全局异常处理器的意义是为了解耦,减少在controller层大量的异常处理代码。其次是将晦涩难懂的异常以更加友好的,我定义的形式返回给用户。在实现上当时有两种方案,一种是实现HandlerExceptionResolver接口中的resolveException这个方法。另一个是基于注释的,使用@ControllerAdvice配合@ExceptionHandler一种声明式的异常处理,我之后改用了后者,因为后者使用@ExceptionHandler可以专门处理一种异常,维护性更加高。
首先我定义了一个统一的API响应格式ApiResult<T>,包含三个核心字段:code(业务状态码)、message(用户可读的信息)、data(成功时返回的数据)。所有接口,无论是正常还是异常,都遵循这个格式。之后

如果你在多个@ControllerAdvice类中定义了处理同一个异常的方法,它们的执行顺序是什么?如何控制?
可以使用@Order来声明优先级,没有声明的话执行顺序是不确定的

4.接入微信支付和支付宝支付微信支付和支付宝支付,完成文章的付费阅读;并通过分布式锁+幂等来防止支付记录布式锁+幂等来防止支付记录的重复写入和更新的重复写入和更新;在支付完成后会给作者发送邮件通知,给用户发送异步消息以解锁付费内容。

5.通过Redis zset 实现用户活跃度排行榜,采用先写 MySQL,再删除 Redis的方案保障高并发场景下的缓存一致性。还利用了 Cannal
实时监听数据库变更,进一步增加数据的可靠性。

好的,关于这个业务流程首先是统计用户当天和当月的活跃度,这里我采用redis进行记录,会以两个key作为幂等,一个是当天的日期和用户名作为当日的幂等,另一个是以月份和用户作为月份记录的幂等,我们会把用户点赞,评论,收藏等消息记录到redis中,为了保证消息的一致性,我这些消息会被先记录到mysql中,之后再同步到redis中。每次查询消息都是先查询redis中的内容,查不到在查数据库中的内容并记录到redis中。

极客社区项目技术点整理 success
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇