苍穹外卖技术点总结(面试)
文章目录
- 苍穹外卖技术点总结(面试)
- 1.使用JWT令牌技术实现员工登录身份验证功能,采用自定义拦截器完成用户认证,通过ThreadLocal配合拦截器进行token的校验,判断用户是否处于登录状态。
- 2.使用Redis数据库,运用SpringCache框架,实现对用户登录信息以及用户查询过菜单数据的缓存,提升用户的访问速度。
- 3.使用缓存解决一致性问题,在系统中采用主动更新加超时删除的缓存更新方案,满足较高的数据一致性。
- 4.使用Maven进行开发,利用其继承特性管理共有jar包的依赖,利用其聚合特性拆分后台管理系统,实现分层工程开发。
- 5.使用Nginx部署前端页面并配置方向代理,实现前后端分离。
- 6.通过WebSocket实现客户端与服务端的长连接,并实现来单提醒及客户催单功能。
- 7.使用SpringTask实现订单状态的定时处理,超时自动取消订单功能。
- 其它
1.使用JWT令牌技术实现员工登录身份验证功能,采用自定义拦截器完成用户认证,通过ThreadLocal配合拦截器进行token的校验,判断用户是否处于登录状态。
JWT令牌的定义及其作用
- 定义:JWT(JSON Web Token)是一种用于身份验证和授权的开放标准。它由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。其中,签名是用于验证令牌的完整性和可信任性。
- 作用:JWT 令牌主要用于实现一种无状态的认证机制,定义了一种紧凑且自包含的方式,在各方之间安全的传输信息,主要用于用户首次登录成功以后,服务器会创建一个 JWT,将其发回给用户,随后用户的每次请求都会包含这个 JWT。JWT 使得服务器无需去存储用户的登录状态,从而实现无状态认证。
ThreadLocal 作用,如何保证数据安全?
- ThreadLocal: ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。
- 作用:ThreadLocal 为每一个线程提供一个单独的存储空间,具有线程隔离的作用,只有在同一个线程内才可以获得他的值,以保证线程安全。
- 在本次开发中,在对 JWT 令牌进行解析,获得当前请求的用户 ID 以后将该 ID 保存至 ThreadLocal 中,以便在之后的操作中查看当前用户。
ThreadLocal的实现原理
- Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,即每个线程都有一个属于自己的ThreadLocalMap。
- ThreadLocalMap内部维护着Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型值。
- 每个线程在往ThreadLocal里设置值的时候,都是往自己的ThreadLocalMap里存,读也是以某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
2.使用Redis数据库,运用SpringCache框架,实现对用户登录信息以及用户查询过菜单数据的缓存,提升用户的访问速度。
讲讲 redis,它在你的项目中作用是什么?
- Redis 是高性能的,基于键值对的,写入缓存的 内存存储系统。它支持多种数据结构如字符串、哈希表、列表、集合、有序集合等,并提供了丰富的操作命令。项目中引入 Redis 的地方是:查询店铺营业状态 ,像这种店铺营业状态,本项目无非就两个状态:营业中/打样。而且它属于高频查询。只要用户浏览到这个店铺,前端就要自动发送请求到后端查询店铺状态。Redis 是基于键值对这种形式存储的,而且 Redis 也把将数据放到缓存中,而不是磁盘,有效缓解了这种高频查询给磁盘带来的压力。
SpringCache
- SpringCache 是 Spring 框架提供的一个抽象层,旨在提供一种透明的方式来缓存应用中的数据。SpringCache 不是一个具体的缓存实现,而是一个集成不同缓存解决方案的接口,如 EHCache、Caffeine、Guava、Redis 等。它允许开发者通过简单的注解来控制方法的缓存行为,例如,使用
@Cacheable
来标记一个方法的返回值应该被缓存,以及使用@CacheEvict
来标记何时移除缓存。SpringCache 为应用提供了一致的缓存视图,而开发者不需要关心具体使用哪种缓存技术。 - 简单的说:它也是一种缓存技术,使得所用工具不局限于 Redis。相比较于使用 Redis 的时候需要把相关代码内嵌到方法体种,Spring Cache 是一种基于注解方式来达到内嵌代码相同的效果。
项目中的应用
存在问题: 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。
结果: 系统响应慢、用户体验差
通过Redis来缓存菜品数据,减少数据库查询操作。
缓存逻辑分析:
- 每个分类下的菜品保存一份缓存数据
- 数据库中菜品数据有变更时清理缓存数据
- Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
- Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如: EHCache、 Caffeine、Redis(常用)
- 为了保证数据库和Redis中的数据保持一致,修改管理端接口 DishController 的相关方法,加入清理缓存逻辑。
3.使用缓存解决一致性问题,在系统中采用主动更新加超时删除的缓存更新方案,满足较高的数据一致性。
在你的项目中 redis 作为缓存, MySQL 的数据如何与 redis 进行同步呢?
- 苍穹外卖这个项目中用户在查看店铺状态时,需要让数据库与 redis 高度保持一致,因为如果店铺没有营业的话就不能点单了,所以它要求时效性比较高,所以采用的读写锁保证的强一致性。我们采用的是 redisson 实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写,读读都互斥,这样就能保证在写数据的同时是不会让其他线程读数据的,避免了脏数据。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。
怎么保证在同时操作多张数据库表出现程序错误时保证数据的一致性?
我在涉及多表操作时使用了事务(Transaction): 将涉及到的数据库操作封装在一个事务中。在事务中,要么所有的数据库操作都成功提交,要么全部失败回滚,保证了数据的一致性。如果发生异常,可以通过捕获异常并执行回滚操作来保证数据的一致性。
具体操作:
- 在启动类上方添加@EnableTransactionManagement
- 开启事务注解之后,我们只需要在需要捆绑成为一个事务的方法上添加@Transactional
- 这样就把对两张表的操作捆绑成为了一个事务。
4.使用Maven进行开发,利用其继承特性管理共有jar包的依赖,利用其聚合特性拆分后台管理系统,实现分层工程开发。
5.使用Nginx部署前端页面并配置方向代理,实现前后端分离。
Nginx 负载均衡和正向、反向代理
- 负载均衡:
Nginx 的负载均衡是将访问请求分发到多个服务器上,以达到平衡服务器负载和提高系统可用性的目的。 - 正向代理:
正向代理是客户端发送请求后通过代理服务器访问目标服务器,代理服务器代表客户端发送请求并将响应返回给客户端。正向代理隐藏了客户端的真实身份和位置信息,为客户端提供代理访问互联网的功能。 - 反向代理:
反向代理是指代理服务器接收客户端的请求,然后将请求转发给后端服务器,并将后端服务器的响应返回给客户端。反向代理隐藏了服务器的真实身份和位置信息,客户端只知道与反向代理进行通信,而不知道真正的服务器。
6.通过WebSocket实现客户端与服务端的长连接,并实现来单提醒及客户催单功能。
后端如何与商家端建立链接,实现实时通信?
-
使用 Websocket 来实现用户端和商家端通信:WebSocket 是一种在 Web 应用程序中实现双向通信的协议。它允许客户端和服务器之间建立持久的、双向的通信通道,使得服务器可以主动向客户端推送消息,而无需客户端发送请求。客户端和服务器之间可以实时地发送消息和接收消息,不需要频繁地发起请求。这样可以减少网络流量和延迟,并提供更好的用户体验。
-
当客户支付后,调用WebSocket的相关API实现服务端向客户端推送消息
-
客户端浏览器解析服务端推送的消息,判断是来单提醒还是客户催单,进行相应的消息提示和语音播报
-
约定服务端发送给客户端浏览器的数据格式为JSON,字段包括:type,orderId,content
- type 为消息类型,1为来单提醒 2为客户催单
- orderId 为订单id
- content 为消息内容
Websocket 与 HTTP 有什么区别? 既然 WebSocket 支持双向通信,功能看似比 HTTP 强大,那么是不是可以基于 WebSocket 开发所有的业务功能?
HTTP 协议和 WebSocket 协议对比:
- HTTP 是短连接
- WebSocket 是长连接
- HTTP 通信是单向的,基于请求响应模式
- WebSocket 支持双向通信
- HTTP 和 WebSocket 底层都是 TCP 连接
不能使用 WebSocket 并不能完全取代 HTTP,它只适合在特定的场景下使用,原因如下:
- 资源开销:WebSocket 需要保持持久连接,对服务器资源有更高要求,不适合所有场景。
- 功能与约定:HTTP 提供丰富的功能和约定(如状态码、缓存控制),适合更广泛的业务需求。
- 安全性和兼容性:虽然 WebSocket 支持加密,但管理安全性可能更复杂;且某些环境下 WebSocket 不被支持或有限制。
- 设计和实践:RESTful API 和相关的 HTTP 设计原则不易直接应用于 WebSocket。
7.使用SpringTask实现订单状态的定时处理,超时自动取消订单功能。
spring task 处理定时任务
- Spring Task(Spring 任务调度)是 Spring 框架提供的一种任务调度框架,用于执行定时任务、异步任务、任务监听、任务调度等。
在苍穹外卖项目中使用 Spring task 用来执行定时任务查看有没有已经派送结束的订单但状态没有更改为已完成,在第二天的固定时间统一调用该定时任务去更改需要调整的订单状态。
其它
讲讲什么是 Httpclient:
- Httpclient是一个服务器端进行 HTTP 通信的库,他使得后端可以发送各种 HTTP 请求和接收 HTTP 响应,使用 HTTPClient,可以轻松的发送 GET, POST, PUT, DELETE 等各种类型的的请求。
在我们的项目中,在进行微信登录开发时,后端在使用登录凭证校验接口的时候就需要发送指定请求到给定的 URL 中。因此我们使用 Httpclient 去完成该任务。
什么是 AOP?
- 概念:
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来,以提高代码的模块化性、可维护性和复用性。- 横切关注点:
比如日志、事务、安全性等,这些关注点会横跨多个模块,导致代码重复、耦合性增加、难以维护等问题。AOP 通过将这些横切关注点抽象成一个个“切面”(Aspect),并将其独立于业务逻辑之外,以达到解耦的目的。
- 横切关注点:
- AOP 的核心概念包括以下几个要素:
- 切面(Aspect): 切面是横切关注点的抽象,它包含了一组横切关注点以及在何时何处应用这些关注点的逻辑。通常,切面由一组通知(Advice)和一个切点(Pointcut)组成。
- 通知(Advice): 通知是切面中具体的逻辑实现,它定义了在何时何地执行横切关注点的具体行为,包括“前置通知”(Before Advice)、“后置通知”(After Advice)、“环绕通知”(Around Advice)等。
- 切点(Pointcut): 切点是在程序中指定的某个位置,通知将在这些位置执行。切点可以使用表达式或其他方式进行定义,以便匹配到程序中的特定方法或代码块。
- 连接点(Join Point): 连接点是在程序执行过程中可以应用通知的具体位置,通常是方法调用、方法执行或异常抛出等。
- 织入(Weaving): 织入是将切面逻辑应用到目标对象中的过程,可以在编译时、加载时或运行时进行。织入可以通过源代码修改、字节码操作、动态代理等方式实现。