牛客社区

282 阅读33分钟

1. 大纲

仿牛客论坛讨论社区主要功能(侧重于后端,前端页面简化)

  1. 帖子展示(按时间/热度),帖子发布及敏感信息审核
  2. 登陆--邮箱验证码,修改头像/密码
  3. 帖子详情,回复、点赞
  4. 置顶、删除帖子的权限控制
  5. 发私信,查看私信
  6. 系统通知:评论/关注/点赞
  7. 全站搜索
  8. 一段时间内网站UV、活跃用户

技术架构

  1. Spring Boot
  2. Spring、Spring MVC、MyBatis(SSM)
  3. Redis、Kafka、Elasticsearch
  4. Spring Security、Spring Actuator

2. 开发环境

  1. 构建工具:Apache Maven:存放构件的位置(mirror 配置镜像仓库)

  2. 集成开发工具:IntelliJ IDEA

  3. 数据库:MySQL、Redis

  4. 应用服务器:Apache Tomcat

  5. 版本控制工具:Git

3. Spring Boot入门,开发社区首页

3.1 Spring Core:

  1. IoC
    • Inversion of Control:控制反转,是一种面向对象编程的设计思想
    • Dependency Injection:依赖注入,是IoC的实现方式
    • IoC Container:IoC容器,是实现DI的关键,本质是一个工厂:ApplicationContext
  2. AOP
  3. 基本组件
    • @SpringBootApplication:配置文件
    • @ContextConfiguration:配置类
    • @SpringBootConfiguration:Spring配置
    • @SpringAutoConfiguration:自动扫描
    • @ComponentScan:扫描本包和子包下Bean
  4. 四个可以让Bean被扫描的注释
    • @Component:通用
    • @Controller:处理请求组件
    • @Service:业务组件
    • @Repository:数据库访问组件
    • 给Bean起名:@Component("name")
  5. Bean的操作
    • @PostConstruct:在建造之前初始化
    • @PreDestroy:在销毁之前
    • @Bean:方法名就是Bean的名字
    • @Autowired:容器给变量自动注入属性
  6. 综合流程
    1. Controller处理浏览器请求
    2. Controller调用业务组件Service
    3. Service调用dao访问数据库

3.2 Spring Data Access

  • Transactions
  • Spring MyBatis

3.3 Web Servlet

Spring MVC

  1. HTTP
    • HyperText Transfer Protocol超文本传输协议
    • 用于传输HTML等内容的应用层协议,规定了浏览器和服务器之间如何通信,以及通信时的数据格式
    • mozilla
  2. Spring MVC Web Server
    • 三层架构:表现层,业务层,数据访问层
    • MVC:Model模型层,View视图层,Controller控制层(都在表现层)
    • 核心组件:DispatcherServlet前端控制器,处理浏览器请求,根据注解路径,调用Controller。Controller把数据封装在Model中返回给前端控制器,再调用View Template生成HTML,最后响应给浏览器。

image.png

Servlet engine

  1. Thymeleaf
    • 模版引擎,以HTML文件为模版,生成动态HTML
    • 常用语法:标准表达式,判断与循环,模版的布局等

3.4 Integration

  • Email
  • Scheduling
  • AMQP
  • Security

3.5 MyBatis入门

安装MySQL + 安装客户端MySQL workbench,连接数据库

MyBatis核心组件

  1. SqlSessionFactory:用于创建SqlSession的工厂类
  2. SqlSession:MyBatis的核心组件,用于向数据库执行SQL
  3. 主配置文件:XML配置文件,可以对MyBatis的底层行为做出详细的配置:DataSourceProperties + MyBatisProperties
  4. Mapper接口:就是DAO接口,在MyBatis中习惯性称为Mapper
  5. Mapper映射器:用于编写SQL,并将SQL和实体类映射的组件,采用XML,注解均可实现

前3项是核心,但已经被Spring Boot整合,不需要配置。SqlSeesion可以自动创建,主配置文件可以在application.properties中配置。主要开发Mapper

数据库中用户数据包括:

  1. id:唯一
  2. username:唯一
  3. password:经过加密后,存入数据库
  4. salt:防止用户密码过于简单,防止破解,在密码后拼接5位随机字符串,之后再加密
  5. email
  6. type:0/1/2 用户类型:普通用户/管理员/版主
  7. status:0/1 激活/未激活
  8. activation_code:激活码
  9. header_url:头像访问路径 10.create_time

增删改查开发步骤:

  1. 在entity层中创建对象,并且属性与数据库一一对应,并提供get + set + toString(方便答应对象,查看数据) 方法。驼峰命名 image.png

  2. 访问数据库,在dao层中创建数据访问接口interface,只需要接口,不需要实现类。@Mapper注解,提供数据操作的方法 image.png

  3. 在mapper层中,为每一个方法创建XML配置文件,写出sql,Mybatis自动提供实现类。 image.png id是方法的名字,resultType是查询后返回的类型,在框中写sql,其中#{}中写参数名称来接受对应参数

3.6 开发社区首页

目标:1. 开发社区首页,显示前10个帖子 2. 开发分页组件,分页显示所有帖子

开发步骤:

  1. 数据库建表 discuss_post:DDL(Data Definition Language) image.png

  2. 开发dao层,在entity层中,新建实体类,用驼峰命名,对应标中字段,并生成get set toString方法

    image.png

  3. 在dao层中,创建mapper接口。注意:@Param注解用于给参数起别名,如果只有一个参数,且在 <if> 中使用,则必须加别名

    image.png

  4. 在service层中,新建service class,使用@Service注解,注入mapper。数据查询后的过滤,筛选等业务层面的处理,可以在service中完成。

    image.png

  5. 在mapper层中,新建xml文件,插入sql。考虑特殊情况:

    • 拉黑不显示:status !=2
    • userId判断不是空<if>
    • order by type, create_time
    • limit

    image.png

  6. 导入静态资源:css + img + js 和html文件

  7. 在controller层中,创建新的controller, 注入service(@Autowired,方法调用前,SpringMVC会自动实例化,所以thymeleaf中可以直接访问对象中的数据),并添加访问路径。把查询出的数据,装入到model中,并返回。

    image.png

  8. 创建新的entity - page,封装分页相关信息

    image.png 特殊判断:

    • 当前页码 >= 1
    • 显示上限 >= 1 && <= 100
    • 获取总页数,起始页码,终止页码
  9. 修改html文件,展示数据库中的数据,并添加分页逻辑

3.7 项目调试

  1. HTTP响应状态吗含义

    image.png

  2. 服务端断点调试:ide

  • 打断点,debug模式启动
  • F8 向下执行一行
  • F7 进入方法
  • F9 程序执行到下一个断点,没有则直接执行完毕
  1. 客户端断点调试:网页检查

  2. 设计日志级别,并将日志输出到不同终端

    image.png logger日志等级

    • debug
    • info
    • error

3.8 版本控制

  1. Git简介

  2. Git常用命令

    • 提交代码
    • 上传代码

    image.png

  3. IDEA集成Git

4. Spring Boot实践,开发社区登陆模版

4.1 发送邮件

  1. 邮箱设置
  • 启用客户端SMTP服务:提供可靠且有效的电子邮件传输的协议,客户端和服务器建立TCP连接
  1. Spring Email
  • 导入jar包
  • 邮箱参数配置:application.properties配置邮箱地址/端口/用户名/密码/协议smtps
  • 使用JavaMailSender发送邮件:创建工具类,定义方法,设置发件人/收件人/内容
  1. 模版引擎
  • 使用Thhymeleaf发送HTML邮件:创建html文件,使用html格式写内容,好处是可以动态地修改内容,对于不同的用户,显示不同的用户名。利用TemplateEngine模版引擎,新建对象,给html文件传入参数

4.2 开发注册功能

  1. 访问注册页面
  • 点击顶部区域内的链接,打开注册页面:
    • 修改html文件,将首页中的注册按钮点击后的访问路径变成注册页面
  1. 提交注册数据
  • 通过表单提交数据:html中提交的方式post,和提交的url地址
  • 服务端验证账号是否已存在,邮箱是否已经注册(Service)
    • 创建util工具类,生成随机字符串
    • 创建util工具类,使用MD5加密,MD5对于同一字符串加密后的密码也一致,且只能加密不能解密。所以对于密码,随机生成5位字符串salt,拼接原密码后加密。DigestUtils.md5DigestAsHex(key.getBytes())
    • 判断用户名/密码/邮箱是否为空,空返回错误
    • 在数据库中查询用户名/邮箱是否存在,存在返回错误。用户名和邮箱不能重复
    • 注册用户,密码加salt后MD5加密后存入数据库
    • 生成随机验证码
    • 分配随机头像
    • 把用户信息插入数据库
  • 服务端发送激活邮件(Service)
    • 使用thymeleaf生成动态html文件th:text="${...}" ,加入url链接,链接到指定路径,动态地拼出用户的id(插入用户时,Mybatis自动生成id)和激活码
    • 生成templateEngine生成html文件
    • 调用mailClient发邮件,发送邮件后跳转主页
  • Controller层:
    • 接收前段页面传入的用户名,密码,邮箱,并调用Service层中的创建新用户的方法
  1. 激活注册账号
  • 点击邮件中的链接,访问服务端的激活服务
    • 用户点击链接后,会传入用户id和激活码(因为刚才的动态html文件中的url路径动态地拼出id和激活码)
    • 在数据库中按用户名查询,对比库中的激活码和传入的激活码
    • 判断,用户状态是否为已激活/激活码不正确
    • 一致后,修改用户状态为已激活,并返回成功信息,跳转html

4.3 会话管理

  1. HTTP性质
  • HTTP是简单的
  • HTTP是可扩展的
  • HTTP是无状态的,有会话的
  1. Cookie
  • 是服务器发送到浏览器,并保存在浏览器端的一小块数据

  • 浏览器下次访问该服务器时,会自动携带该数据,将其发送给服务器

  • 解决HTTP请求之间无关联的问题,创建有状态的会话。使用cookies让每次请求共享相同的上下文信息,达成相同的状态。例子:购物车中添加东西,第二次带cookie可以获取第一次添加到购物车的东西

  • 默认存到内存,关闭浏览器就失效,但是设置cookie生效时间后,会存入硬盘,有效期直到时间结束

    image.png

  1. Session
  • 是JavaEE的标准,用于在服务端记录客户端信息

  • 数据存放在服务端更加安全,但也会增加服务端的内存压力

  • 分布式部署session会有问题,客户端统一访问nginx,nginx分配给不同服务器。Session存在不同服务器中,不能识别

    • 解决1粘性session:同一个ip分配给同一个服务器,但可能负载不均衡
    • 解决2同步session:某一个服务器创建session后,同步给所有服务器,但影响服务器性能且服务器之间有耦合
    • 解决3共享session:单独一台服务器存放session,但如果宕机,所有服务器收到影响
    • 解决4不存服务器,存数据库,数据库集群,服务器在数据库中取session,但性能更慢
    • 解决5存redis:推荐!

    image.png

4.4 生成验证码

Kaptcha

  • 导入jar包:可以生成随机字符和图片
  • 编写Kaptcha配置类:实例化接口
  • 生成随机字符,生成图片:设置图片大小,字符大小/颜色/长度
  • 生成验证码,将验证码存入session,将图片输出给浏览器
  • 修改html文件,设置动态路径,将验证码图片插入
  • 检查输入的验证码与session中存放的是否一致

4.5 开发登陆,退出功能

  1. 访问登陆页面
  • 点击顶部区域内的链接,打开登陆页面
  1. 登陆
  • 验证账号、密码、验证码
  • 成功时,生成登陆凭证,发放给客户端
  • 失败时,跳转回登陆页
  1. 退出
  • 将登陆凭证修改为失效状态
  • 跳转至网站首页
  1. 开发过程:登陆
    1. 创建数据表login_ticket:包含id/user_id/ticket/status/expired。ticket是随机字符串,作为登陆凭证,发送给浏览器让浏览器保存cookie,再次登陆时,cookie携带ticket,在数据库查询后可以知道是那个用户在登录,且这个用户的登陆状态有没有过期
    2. Mapper层
      • 插入数据:用户,ticket,状态,过期时间
      • 通过ticket查询数据
      • 修改用户状态-0代表正常登陆1代表退出,一般不直接删除数据
    3. Service层
      • 验证用户登陆:验证账户不为空,密码不为空,账号激活状态,验证密码(前端输入密码+salt再次通过md5加密后的值,与user表中保存的值对比)
      • 生成登陆凭证,随机生成字符串。生成过期时间,存入login_ticket表中
      • 返回ticket的值
    4. Controller层
      • 验证码是否正确,不管大小写。在contoller层判断,service不需要管。提高效率,不需要访问数据库
      • 调用service层,检查账号密码以及过期时间。正确把ticket作为cookie发给客户端保存,并返回首页界面,错误返回登陆页面
    5. 修改html 文件
  2. 开发过程退出:传入ticket,在login_ticket表中查询ticket对应用户,将用户状态改为未登陆

4.6 显示登陆信息

  1. 拦截器示例
  • 拦截器浏览器的请求,以很低的耦合度,批量解决共有业务
  • 定义拦截器,实现HandlerInterceptor
  • 配置拦截器,为它制定拦截、排除的路径
  1. 拦截器应用
  • 在请求开始时查询登陆用户

  • 在本次请求中持有用户数据

  • 在模版视图上显示用户数据

  • 在请求结束时清理用户数据

    image.png 每次请求都需要走这个逻辑,template负责渲染用户信息,包括用户名和用户头像等。根据ticket找user,然后给template渲染用户信息

    使用ThreadLocal作为容器,持有用户信息,代替session对象。好处:在同一线程中很方便的获取用户信息,不需要频繁的传递session对象

  1. HandlerInterceptor
  • preHandle:在Controller之前执行
  • postHandle:在Controller之后执行
  • afterCompletion:在TemplateEngine之后执行

4.7 账号设置

  1. 上传文件
  • 请求:必须是POST
  • 表单:enctype= "multipart/form-data"
  • Spring MVC:通过MultipartFIle处理上传文件
  1. 开发步骤
  • 访问账号设置页面
  • 上传头像
    • 文件存入硬盘,设置存放路径。不需要存入数据库,所以不开发dao层mapper
    • 判断文件格式,并给上传的文件生成随机文件名+原始后缀
    • 数据库中更新当前用户的头像web路径,服务器存放路径中获取文件名,从而得到web路径
  • 获取头像

4.8 检查登陆状态

  1. 使用拦截器
  • 在方法前标注自定义注解
  • 在请求最开始,判断是否登陆,看能否获取当前用户。没登陆屏蔽部分功能,如个人信息,个人收件箱等
  • 拦截所有请求,只处理带有该注解的方法
  1. 自定义注解
  • 常用的元注解:@Target/@Retention/@Document/@Inherited
  • 如何读取注解:Method.getDeclaredAnnotations()/Method.getAnnotation(Class<T> annotaionClass)

5. Spring Boot实践,开发社区核心功能

5.1 敏感词过滤

  1. 前缀树

    image.png

  • 名称:Trie/字典树/查找树
    • 根结点没有字符,除了根结点,每个子节点只有一个字符
    • 从根结点到叶子节点连起来是一个字符串,如果连起来是敏感词,则在叶子节点标记
    • 同级子节点,字符唯一
  • 特点:查找效率高,消耗内存大
    • 字符串查询敏感词,需要3个指针
    • 指针1:指向字典树的根节点,作为查询字典的指针
    • 指针2:从左到右指向字符串,作为单词的起始位置,不回头
    • 指针3:从指针2的位置开始,往后移动,作为单词的终止位置,每次指针2移动时,回头
    • 建立StringBuilder,如果字符不是敏感词就直接加入,如果是则用*代替,指针2跳转,指针1回root
  • 应用:字符串检索/词频统计/字符串排序
  1. 敏感词过滤器
  • 定义前缀树
  • 根据敏感词,初始化前缀树:注意要跳过奇奇怪怪的符号,只存汉字字符。0x2E80-0x9FFF是东亚文字范围
  • 编写过滤敏感词的方法

5.2 发布帖子

  1. AJAX
  • Asynchronous JavaScript and XML
  • 异步的JavaScript和XML,不是一门新技术,只是一个新术语
  • 使用AJAX,网页能将增量更新呈现在页面上,而不需要刷新整个页面
  • 虽然X代表XML,但目前JSON的使用比XML更普遍,JSON更好解析
  1. 示例
  • 使用jQuery发送AJAX请求:在html中设置一个小button,每次点击时,发送异步请求POST,在不需要刷新整个页面的情况下,访问服务器,并获取数据
  1. 实践
  • 采用AJAX请求,实现发布帖子的功能:过滤帖子敏感词,并插入数据库

5.3 帖子详情

  1. DiscussPostMapper
  2. DiscussPostService
  3. DiscussPostController
  4. index.html:在帖子标题上增加访问详情页面的链接
  5. discuss-detail.html:处理静态资源的访问路径/复用index.html的header区域/显示标题,作者,发布时间,帖子正文等内容

5.4 事务管理

  1. 事务:

image.png 2. 事务的隔离性

  • 常见的并发异常

    • 第一类丢失更新:某一个事务回滚,导致另一个事务已更新的数据丢失

      image.png

    • 第二类丢失更新:某一个事务提交,导致另一个事务已更新的数据丢失

      image.png

    • 脏读:某一个事务,读取了另外一个事务未提交的数据

      image.png

    • 不可重复读:某一个事务,对同一个数据前后读取的结果不一致

      image.png

    • 幻读:某一个事务,对同一个表前后查询到的行数不一致

      image.png

  • 常见的隔离级别

    • Read Uncommitted:读取未提交的数据

    • Read Committed:读取已提交的数据

    • Repeatable Read:可重复读

    • Serializable:串行化

      image.png

  1. 实现机制
  • 悲观锁(数据库默认)

    • 共享锁(S锁):一个事务加了共享锁后,其他事务只能对该数据加共享锁,但不能加排他锁。能读不能改
    • 排他锁(X锁):一个事务加了共享锁后,其他事务不能加共享锁,也不能加排他锁。不能读不能改
  • 乐观锁(自定义)

    • 版本号/时间戳等:在更新数据前,检查版本号是否发生变化。若变化则取消本次更新,否则就更新数据(版本号+1)
  1. Spring事务管理
  • 声明式事务
    • 通过XML配置,声明某方法的事务特征
    • 通过注解,声明某方法的事务特征
  • 编程式事务
    • 通过TransactionTemplate管理事务,并通过它执行数据库的操作

5.5 显示评论

  1. 数据层
  • 根据实体查询一页评论数量:根据 entity_type 和 entity_id 查询,limit数量
    • 创建comment表
    • id
    • 发帖user_id
    • entity_type:1帖子/2评论
    • entity_id:帖子/评论id
    • target_id:指向哪个用户
    • content
    • status
    • create_time
  • 根据实体查询评论的数量:根据 entity_type 和 entity_id 查询,count
  1. 业务层
  • 处理查询评论的业务
  • 处理查询评论数量的业务
  1. 表现层
  • 显示帖子详情数据时,同时显示该帖子所有的评论数据:简历hashmap,存放人和对应的评论

5.6 添加评论

  1. 数据层
  • 增加评论数量
  • 修改帖子的评论数量
  1. 业务层
  • 处理添加评论的业务
  • 先增加评论、再更新帖子的评论数量
  1. 表现层
  • 处理添加评论数据的请求
  • 设置添加评论的表单

5.7 私信列表

  1. 私信列表
  • 查询当前用户的会话列表,每个会话只显示一条最新的私信
  • 支持分页显示
  1. 私信详情
  • 查询某个会话所包含的私信
    • 创建数据表message
    • 会话id
    • from_id
    • to_id
    • content
    • status
    • create_time
  • 支持分页显示

5.8 发送私信

  1. 发送私信
  • 采用异步的方式发送私信
  • 发送成功后刷新私信列表
  1. 设置已读
  • 访问私信详情时,将显示的私信设置为已读状态

5.9 统一异常处理

表现层->业务层->数据层。会把异常给表现层,所有异常在表现层统一处理。控制台记录日志,前端页面重定向到专门的错误页面。

  1. @ControllerAdvice
  • 用于修饰类,表示该类时Controller的全局配置类
  • 在此类中,可以对Controller进行三种全局配置
    • 异常处理方案
    • 绑定数据方案
    • 绑定参数方案
  1. @ExceptionHandler
  • 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常
  1. @ModelAttribute
  • 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数

4.@DataBinder

  • 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器

5.10 统一记录日志

  1. 记录日志是系统需求,而不是业务需求。不要编码到业务方法里面。记录什么用户在什么时间访问了什么方法

解决方法:AOP,Aspect OrientedProgramming 面向切面编程。是对OOP(面向对象编程)的补充,可以进一步提高编程效率

image.png

横向封装系统需求的方法,使每个业务组件都可以调用,降低耦合

  1. AOP术语

image.png

  • target:目标对象,业务组件处理需求的目标
  • aspect:封装业务需求的对象,面对aspect编程
  • weaving:把aspect织入target
    • Pointcut:切点声明,声明要把aspect织入什么target的什么位置
    • Advice:实现具体织入逻辑
  • Joinpoint:目标对象可以织入代码的位置
  1. AOP实现
  • AspectJ

    • AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法
    • AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件
    • 功能强大,但学习成本高,因为是一门新的语言
  • Spring AOP

    • Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器

    • Spring AOP在运行时通过代理(用一个对象代替原始对象,调用代理对象)的方式织入代码,只支持方法类型的连接点

      • Spring AOP的代理1: JDK动态代理:Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用蛰虫方式,在接口的代理实例中织入代码
      • Spring AOP的代理2:CGLib动态代理:采用底层的字节码技术,在运行时创建子类代理实例。当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码
    • Spirng支持对AspectJ的集成

6. Redis,一站式高性能存储方案

6.1 Redis入门

  • Redis是一款基于键值对的NoSQL数据库,它的值支持多种数据结构:字符串String,哈希hash,列表list,集合yset,有序集合sorted set 等
  • Redis将所有的数据都存放在内存中,所以读写性能十分惊人。同时,Redis还可以将内存中的数据以快照或日志的形式保存到硬盘上,以保证数据的安全性
  • Redis典型的应用场景包括:频繁访问缓存/排行榜/计数器/社交网络/消息队列等

6.2 Spring整合Redis

  1. 引入依赖:spring-boot-starter-data-redis

  2. 配置Redis

  • 配置数据库参数

  • 编写配置类,构造RedisTemplate

    image.png

  1. 访问Redis:redis保证事务reidsTemplate.execute(new SessionCallback)
  • redisTemplate.opsForValue() -- string
  • redisTemplate.opsForHash()
  • redisTemplate.opsForList()
  • redisTemplate.opsForSet()
  • redisTemplate.opsForZset()

6.3 点赞

  1. 点赞:性能要求高,所以用Redis储存在内存中
  • 支持对帖子、评论点赞
    • 记录某个实体的赞(评论/帖子)
    • key:like:entity:entityType:entityId
    • value:set(userId)。记录点赞的人,数量用set.size()统计。只用Integer的话,不能知道是谁点赞了
    • 异步请求
  • 第一次点赞,第二次取消:查询key是否存在,存在删除,不存在添加
  1. 首页点赞数量
  • 统计帖子点赞数量:

3.详情页点赞数量

  • 统计点赞数量
  • 显示点赞状态

6.4 我收到的赞

  1. 重构点赞功能
  • 以用户为key,记录点赞数量:注意查询是否点过赞要放在事务之外,因为redis的事务不会立刻执行,事务过程所有命令都在队列中,事务提交再统一执行。所以查询在事务中的话,会导致不能及时得到结果
  • key-increment,key-decrement
  1. 开发个人主页
  • 以用户为key,查询点赞数量

6.5 关注和取关

  1. 需求
  • 开发关注、取关功能

    image.png

  • 统计用户的关注数、粉丝数

    • 查询关注的实体的数量
    • 查询实体的粉丝的数量
    • 查询当前用户是否已经关注该实体
    • 查询关注数
    • 查询粉丝数
    • 查询是否已经关注
  1. 关键
  • 如果A关注了B,则A是B的follower粉丝,B是A的followee目标
  • 关注的目标可以是用户/帖子/题目等,在实现时将目标抽象为实体

6.6 关注列表和粉丝列表

  1. 业务层:直接访问redis

    • 查询某个用户关注的人,支持分页
    • 查询某个用户的粉丝,支持分页
  2. 表现层

  • 处理“查询关注的人”、“查询粉丝”请求
  • 编写“查询关注的人”、“查询粉丝”模版

6.7 优化登陆模块:验证码/登陆凭证/用户信息

  1. 使用Redis存储验证码
  • 验证码需要频繁的访问与刷新,对性能要求较高
  • 验证码不需要永久保存,通常在很短的时间后就失效
  • 分布式部署时,存在Session共享的问题,Redis没有
    • 验证码归属:生成id,保存cookie,设置生效时间
    • 将验证码存入Redis
  1. 使用Redis存储登陆凭证
  • 处理每次请求时,都要查询用户的登陆凭证,访问频率非常高
    • 优先在缓存Redis中取值
    • 取不到时,从MySQL数据库中取值,初始化缓存数据
    • 数据变更时清除缓存数据
  1. 使用Redis缓存用户信息
  • 处理每次请求时,都要查询用户信息,访问频率非常高

7. Kafka,构建TB级异步消息系统

7.1 阻塞队列

  1. 阻塞队列 Blocking Queue
  • 解决线程通信的问题
  • 阻塞方法:put/take image.png
  • 实现
    • 点对点模式m:每个数据只被一个消费者消费
    • 发布订阅模式:消息可以被多个不同消费者在不同时间读取(Kafka)
  1. 生产者消费者模式
  • 生产者:产生数据的线程
  • 消费者:使用数据的线程
  • 解决生产者和消费者速度不一样的问题,不影响系统性能,避免CPU资源浪费
  1. 实现类
  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • PriorityBlockingQueue/SynchronousQueue/DelayQueue等

7.2 Kafka入门

  1. Kafka简介
  • Kafka是一个分布式的流媒体平台
  • 应用:消息系统/日志收集/用户行为追踪/流式处理
  1. Kafka特点
  • 高吞吐量:TB级别
  • 消息持久化:硬盘存储。硬盘读写速度取决于使用方法,硬盘顺序读写效率很高
  • 高可靠性:分布式保证
  • 高扩展性:可以加服务器
  1. Kafka术语
  • Broker:Kafka的服务器

  • Zookeeper:管理Kafka集群,Kafka内置。需要配置路径

  • Topic:发布订阅模式,存放发布者发布的消息

  • Partition:为了增强并发性能,把一个topic分为多个partition

  • Offset:消息在topic中存放的索引

  • Leader Replica:主副本,可以处理请求,如果挂了,会选择一个从副本作为主副本

  • Follower Replica:从副本,备份主副本数据

    image.png

  1. 创建kafka topic

image.png

  1. 生产和消费消息

image.png

image.png

7.3 Spring整合Kafka

  1. 引入依赖: spring-kafka

  2. 配置Kafka:配置server,consumer

image.png 自动提交offset偏移位置

  1. 访问Kafka
  • 生产者:KafkaTemplate.send(topic,data)
  • 消费者:@KafkaListener(topics = {"test"}),public void handleMessage(ConsumerRecord record) {}监听消息并获取

7.4 发送系统通知

  1. 触发事件
  • 评论/点赞/关注操作频繁,为了提高性能,使用Kafka队列。异步方式

  • 评论后,发布通知:Corntroller中添加操作

    image.png 丢入队列,等待处理

  • 点赞后,发布通知

  • 关注后,发布通知

    image.png

  1. 处理事件
  • 封装事件对象
    • 事件而不是消息,抽象成更高级更普遍的事件,类型可以是评论/点赞/关注
    • 定义发送人和接收人,传输json,包括userid,entityid,entitytype,content
  • 开发事件的生产者
  • 开发事件的消费者

7.5 显示系统通知

把上一节存入数据库中的数据,显示在页面中

  1. 通知列表:显示评论/点赞/关注三种类型的通知

    Controller:

    image.png

  2. 通知详情:分页显示某一类主题包含的通知

  3. 未读消息:在页面头部显示所有未读消息的数量

8. Elasticsearch,分布式搜索引擎

8.1 Elasticsearch简介

  1. Elasticsearch简介
  • 一个分布式的,Restful风格(设计风格,规定前后端http请求标准的描述)的搜索引擎
  • 支持对各种类型的数据的检索
  • 搜索速度快,可以提供实时的搜索服务
  • 便于水平扩展(加服务器),每秒可以处理PB级海量数据
  1. Elasticsearch术语
  • Elasticsearch实现搜索,需要复制一份数据库,所以可以把它看成特殊的数据库
  • 索引:原来指数据库中的库,database。6.0指表table
  • 类型:原来指数据库中的表,table。6.0以后废弃
  • 文档:数据库中的表中的一行rowc,通常是json
  • 字段:数据库中的表中的一列column,一个属性
  • 集群:多个服务器组成
  • 节点:集群中的每一个服务器
  • 分片:一个索引可以拆分成多个分片,提高并发能力
  • 副本:对分片的备份
  1. 分词
  • Elasticsearch只支持英文分词
  • 中文分词需要安装插件:GitHub :elasticsearch ik
  1. Postman: 模拟http请求

8.2 Spring整合Elasticsearch

  1. 引入依赖:spring-boot-starter-data-elasticsearch

  2. 配置:cluster-name/clustter-nodes

  3. spring data elasticsearch

  • ElasticsearchTemplate
  • ElasticsearchRepository
  1. 常用操作
  • save:保存一条数据
  • saveAll:保存所有数据
  • deleteById:删除一条
  • withQuery:构建搜索条件 withQuery(QUeryBuilders.multiMatchQuery("关键词"),"title","content")

image.png

8.3 开发社区搜索功能

  1. 搜索服务
  • 将帖子保存至Elasticsearch服务器
  • 从Elasticsearch服务器删除帖子
  • 从Elasticsearch服务器搜索帖子
  1. 发布事件
  • 发布帖子时,将帖子异步的提交到Elasticsearch服务器
  • 增加评论时,将评论异步的提交到Elasticsearch服务器
  • 在消费组件中增加一个方法,消费帖子发布事件
  1. 显示结果
  • 在Controller中处理搜索请求,在HTML上显示搜索结果

9. 项目进阶,构建安全高效的企业服务

9.1 Spring Security

  1. 简介:Spring Security是一个专注于为Java应用程序提供身份认证和授权的框架,它的强大之处在于可以轻松扩展以满足自定义的需求

  2. 特征

  • 对身份认证授权提供全面的、可扩展的支持
  • 防止各种攻击,如会话固定攻击、点击劫持、csrf等
  • 支持于Servlet API/Spring MVC等web技术集成

image.png

  1. 具体实现
  • AuthenticationManager:认证的核心接口
  • AuthenticationManagerBuilder:用于构建AuthenticationManager对象的工具
  • ProviderManager:AuthenticationManager接口默认的实现类
  • 自定义认证:AuthenticationProvider:ProviderManager持用一组AuthenticationProvider,每个AuthenticationProvider负责一种认证。这种是委托模式,ProviderManager将认证委托给AuthenticationProvider
  • Authentication:用于封装认证信息的接口(这里是账号和密码),不同的实现类代表不同类型的认证信息
  • support:当前的AuthenticationProvider支持那种类型的认证
  • 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中

9.2 权限控制

  1. 登陆检查:之前采用拦截器实现了登陆检查,这是简单的权限管理方案,现在使用security代替

  2. 授权配置:对当前系统内包含的所有请求,分配访问权限(普通用户/版主/管理员)

  3. 认证方案:绕过security认证流程,采用系统原来的认证方案

  4. CSRF配置:防止csrf攻击的基本原理,以及表单/AJAX相关的配置

  • 提交表单时,网站窃取浏览器cookie中的ticket,假装身份发送请求
  • 解决方法:security启用,每次服务器返回数据额外附加一个tooken,网站窃取不到,验证时需要ticket+cookie才可以
  • 发送AJAX请求之前,将CSRF令牌设置到请求的消息头中

image.png

9.3 置顶/加精/删除

9.4 Redis高级数据类型

用于网站统计

  1. HyperLogLog
  • 采用一种基数算法,用于完成独立总数的统计
  • 占据空间小,无论统计多少个数据,只占12k的内存空间
  • 不精确的统计算法,标准误差为0.81%
  • 用于去重统计用户访问数量
  1. Bitmap
  • 不是一种独立的数据结构,实际上就是字符串
  • 支持按位存取数据,可以将其看成byte数组
  • 适合存储大量连续数据的布尔值(true/false)
  • 用于统计用户签到

9.5 网站数据统计

  1. UV(Unique Visitor)
  • 独立访客,需要通过用户IP去重统计数据
  • 每次访问都要统计
  • 统计游客+登陆用户
  • HyperLogLog,性能好,且存储空间小
  1. DAU(Daily Active User)
  • 日活跃用户,需要通过用户IP去重统计数据
  • 访问过一次,则认为其活跃
  • 统计登陆用户
  • Bitmap,性能好,且结果精确

9.6 任务执行和调度

  1. JDK线程池
  • ExecutorService
  • ScheduledExecutorService
  1. Spring线程池
  • ThreadPoolTaskExecutor:普通线程池
  • ThreadPoolTaskScheduler:线程执行定时任务
  1. 分布式定时任务
  • Spring Quartz

Nginx:浏览器任务发给Nginx, 负责服务器负载均衡

9.7 热帖排行

image.png

9.8 长图

9.9 将文件上传到云服务器

  1. 客户端上传
  • 客户端将数据提交给云服务器,并等待其响应
  • 用户上传头像时,将表单数据提交给云服务器
  1. 服务器直接传
  • 应用服务器将数据直接提交给云服务器,并等待其响应
  • 分享时,服务端将自动生成的图片,直接提交给云服务器
  1. 具体实现
  • 使用七牛云服务器

    image.png

9.10 优化网站的性能

加快响应速度有一个常见的方法就是加缓存

  1. 本地缓存
  • 将数据缓存在应用服务器上,性能最好
  • 常用工具:Encache/Guava/Caffeine
  1. 分布式缓存
  • 将数据缓存在NoSQL数据库上,跨服务器
  • 由于网络开销,效率略低于本地缓存··
  • 常用工具:MemCache/Redis
  1. 多级缓存
  • 一级缓存(本地缓存)> 二级缓存(分布式缓存) > DB
  • 避免缓存雪崩(缓存失效,大量请求直达DB),提高系统的可用性

10. 项目发布和总结

8.1 单元测试

image.png

8.2 项目监控

image.png

8.3 项目部署

image.png

8.4 项目总结

image.png

image.png

  1. 介绍

一个基本功能完整的论坛项目。项目主要功能有:基于邮件激活的注册方式,基于 MD5 加密与加盐的密码存储方式,登陆功能加入了随机验证码的验证。实现登陆状态的检查、为游客和已登录用户展示不同界面与功能。实现不同用户的权限控制和网站数据统计(UV、DAU),管理员可以查看网站数据统计和网站监控信息。支持用户上传头像,实现发布帖子、评论帖子、热帖排行、发送私信与敏感词过滤等功能。实现了点赞关注与系统通知功能。支持全局搜索帖子信息的功能

2.核心功能:
- 发帖、评论、私信、转发;
- 点赞、关注、通知、搜索;
- 权限、统计、调度、监控;
3.核心技术:
- Spring Boot、SSM
- Redis、Kafka、ElasticSearch
- Spring Security、Quatz、Caffeine

  1. 核心功能实现
  • 通过对登录用户颁发登录凭证,将登陆凭证存进 Redis 中来记录登录用户登录状态,使用拦截器进行登录状态检查,使用 Spring Security 实现权限控制,解决了 http 无状态带来的缺陷,保护需登录或权限才能使用的特定资源。
  • 使用 ThreadLocal 在当前线程中存储用户数据,代替 session 的功能便于分布式部署。在拦截器的 preHandle 中存储用户数据并构建用户认证的结果存入 SecurityContext,在 postHandle 中将用户数据存入 Model,在 afterCompletion 中清理用户数据。
  • 使用 Redis 的集合数据类型来解决踩赞、相互关注功能,采用事务管理,保证数据的正确,采用“先更新数据库,再删除缓存”策略保证数据库与缓存数据的一致性。 - 采用 Redis 存储验证码,解决性能问题和分布式部署时的验证码需求。采用 Redis 的 HyperLogLog 存储每日 UV、Bitmap 存储 DAU,实现网站数据统计的需求。
  • 使用 Kafka 作为消息队列,在用户被点赞、评论、关注后以系统通知的方式推送给用户,用户发布或删除帖子后向 elasticsearch 同步,wk 生成长图后将长图上传至云服务器,对系统进行解耦、削峰。
  • 使用 elasticsearch + ik 分词插件实现全局搜索功能,当用户发布、修改或删除帖子时,使用 Kafka 消息队列去异步将帖子给 elasticsearch 同步。
  • 使用分布式定时任务 Quartz 定时计算帖子分数,来实现热帖排行的业务功能。
  • 对频繁需要访问的数据,如用户信息、帖子总数、热帖的单页帖子列表,使用 Caffeine 本地缓存 + Redis 分布式缓存的多级缓存,提高服务器性能,实现系统的高可用
  1. 项目亮点:
    - 项目构建在Spring Boot+SSM框架之上,并统一的进行了状态管理、事务管理、异常处理;
    - 利用Redis实现了点赞和关注功能,单机可达5000TPS;
    - 利用Kafka实现了异步的站内通知,单机可达7000TPS;
    - 利用ElasticSearch实现了全文搜索功能,可准确匹配搜索结果,并高亮显示关键词;
    - 利用Caffeine+Redis实现了两级缓存,并优化了热门帖子的访问,单机可达8000QPS。
    - 利用Spring Security实现了权限控制,实现了多重角色、URL级别的权限管理;
    - 利用HyperLogLog、Bitmap分别实现了UV、DAU的统计功能,100万用户数据只需*M内存空间;
    - 利用Quartz实现了任务调度功能,并实现了定时计算帖子分数、定时清理垃圾文件等功能;
    - 利用Actuator对应用的Bean、缓存、日志、路径等多个维度进行了监控,并通过自定义的端点对数据库连接进行了监控。

8.5 面试题

  1. MySQL
  • 存储引擎
  • 事务
  • 索引
  1. Reids
  • 数据类型
  • 过期策略
  • 淘汰策略
  • 缓存穿透
  • 缓存击穿
  • 缓存雪崩
  • 分布式锁
  1. Spring
  • Spring IOC
  • Spring AOP
  • Spring MVC