关与一个SpringBoot单体项目的优化-->外卖平台优化

项目优化

  • 一 缓存优化
    • 1.1 V1.0版本实现缓存的操作
      • 1.1.1 缓存短信验证码
      • 1.1.2 缓存菜品信息
      • 1.1.3 清理菜品缓存
        • 1). 保存菜品,清空缓存 选择方式一:清理当前添加菜品分类下的缓存
        • 2). 更新菜品,清空缓存 选择方式一:清理当前添加菜品分类下的缓存
    • 1.2 V1.1版本 实现套餐缓存的操作
      • 关于SpringCache
        • 1.2.1 介绍
        • 1.2.1 常用注解
      • 项目引入SpringCache
  • 二 读写分离优化
    • 2.1 读写分离概念
      • 2.1.1 数据库主从复制
        • 2.1.1.1 介绍
        • 2.1.1.2 复制原理
        • 环境搭建
    • 2.2 读写分离的案例
      • 2.2.1 背景介绍
      • 2.2.2 关于ShardingJDBC
        • 2.2.2.1 使用步骤
    • 2.3 读写分离引入项目
  • 三 关于nginx部分
    • 3.1 关于nginx常用命令
    • 3.2 重点目录和文件如下
    • 3.3 负载均衡策略
  • 四 前后端分离优化
    • 4.1 开发流程
    • 4.2 关于一个接口依赖swagger
      • 4.2.1 介绍
      • 4.2.1 使用方式
      • 4.2.2 常用注解

一 缓存优化

分析:在原先的代码中,我们将短信验证码保存在session域对象中,将常不变的数据缓存到redis数据库中;

1.1 V1.0版本实现缓存的操作

使用spring提供的redis的提供的模版,也就时redisTemplate去进行操作,通过opsForValue()方法去操作;

1.1.1 缓存短信验证码

思路
1). 在服务端UserController中注入RedisTemplate对象,用于操作Redis;
2). 在服务端UserController的sendMsg方法中,将随机生成的验证码缓存到Redis中,并设置有效期为5分钟;
3). 在服务端UserController的login方法中,从Redis中获取缓存的验证码,如果登录成功则删除Redis中的验证码;

1.1.2 缓存菜品信息

思路
1). 改造DishController的list方法,先从Redis中获取分类对应的菜品数据,如果有则直接返回,无需查询数据库;如果没有则查询数据库,并将查询到的菜品数据存入Redis。

2). 改造DishController的save和update方法,加入清理缓存的逻辑。
注意:
​ 在使用缓存过程中,要注意保证数据库中的数据和缓存中的数据一致,如果数据库中的数据发生变化,需要及时清理缓存数据。否则就会造成缓存数据与数据库数据不一致的情况。
具体实现
1). 在DishController中注入RedisTemplate

@Autowired
private RedisTemplate redisTemplate;

2). 在list方法中,查询数据库之前,先查询缓存, 缓存中有数据, 直接返回

List<DishDto> dishDtoList = null;
//动态构造key
String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus();//dish_1397844391040167938_1
//先从redis中获取缓存数据
dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
if(dishDtoList != null){
    //如果存在,直接返回,无需查询数据库
    return R.success(dishDtoList);
}

3). 如果redis不存在,查询数据库,并将数据库查询结果,缓存在redis,并设置过期时间

//如果不存在,需要查询数据库,将查询到的菜品数据缓存到Redis
redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);

1.1.3 清理菜品缓存

为了保证数据库中的数据和缓存中的数据一致,如果数据库中的数据发生变化,需要及时清理缓存数据。所以,我们需要在添加菜品、更新菜品时清空缓存数据。

1). 保存菜品,清空缓存 选择方式一:清理当前添加菜品分类下的缓存

//清理某个分类下面的菜品缓存数据
String key = "dish_" + dishDto.getCategoryId() + "_1";
redisTemplate.delete(key);

2). 更新菜品,清空缓存 选择方式一:清理当前添加菜品分类下的缓存

//清理某个分类下面的菜品缓存数据
String key = "dish_" + dishDto.getCategoryId() + "_1";
redisTemplate.delete(key);

1.2 V1.1版本 实现套餐缓存的操作

关于SpringCache

1.2.1 介绍

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码。

Spring Cache只是提供了 抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口。

针对不同的缓存技术需要实现不同的CacheManager:
关与一个SpringBoot单体项目的优化-->外卖平台优化_第1张图片

1.2.1 常用注解

关与一个SpringBoot单体项目的优化-->外卖平台优化_第2张图片
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。
解释:
@CachePut注解:
作用: 将方法返回值,放入缓存
​ value: 缓存的名称, 每个缓存名称下面可以有很多key
​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法
key的写法如下: #result,#参数对象.属性
@CacheEvict注解
value: 缓存的名称,每个缓存名称下面可以有多个key
​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法
@Cacheable注解
作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
​ value: 缓存的名称,每个缓存名称下面可以有多个key
​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

项目引入SpringCache

实现思路
1). 导入Spring Cache和Redis相关maven坐标

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-cacheartifactId>
dependency>
2). 在application.yml中配置缓存数据的过期时间
spring:  
  cache:
    redis:
      time-to-live: 1800000 #设置缓存数据的过期时间
3). 在启动类上加入@EnableCaching注解,开启缓存注解功能
4). 在SetmealController的list方法(查询方法)上加入@Cacheable注解
5). 在SetmealController的save和delete方法上加入CacheEvict注解

二 读写分离优化

2.1 读写分离概念

其实就是将数据库分为了主从库,一个主库用于写数据,一个或多个从库完成读数据的操作,主从库之间通过某种机制进行数据的同步(这是一个难点,也是一个很难保证的技术点,即" 数据库读写分离一致性问题 "),这是一种常见的数据库架构

2.1.1 数据库主从复制

2.1.1.1 介绍

		MySQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的 **二进制日志** 功能。就是一台或多台MySQL数据库(slave,即**从库**)从另一台MySQL数据库(master,即**主库**)进行日志的复制,然后再解析日志并应用到自身,最终实现 **从库** 的数据和 **主库** 的数据保持一致。MySQL主从复制是MySQL数据库自带功能,无需借助第三方工具。
		二进制日志(BINLOG)记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,但是不包括数据查询语句。此日志对于灾难时的数据恢复起着极其重要的作用,MySQL的主从复制, 就是通过该binlog实现的。默认MySQL是未开启该日志的。

2.1.1.2 复制原理

关与一个SpringBoot单体项目的优化-->外卖平台优化_第3张图片
MySQL复制过程分成三步:
1). MySQL master 将数据变更写入二进制日志( binary log)
2). slave将master的binary log拷贝到它的中继日志(relay log)
3). slave重做中继日志中的事件,将数据变更反映它自己的数据

环境搭建

准备两台虚拟机(需要安装mysql),可以通过克隆实现,然后进行相应配置:
1). 防火墙开放3306端口号
2). 并将两台数据库服务器启动起来:
3)修改Mysql数据库的配置文件/etc/my.cnf
在最下面增加配置:
log-bin=mysql-bin #[必须]启用二进制日志
server-id=200 #[必须]服务器唯一ID(唯一即可)
重启主库
然后创建数据同步的用户并授权
登录Mysql数据库,查看master同步状态
4)从库配置 修改Mysql数据库的配置文件/etc/my.cnf*
server-id=201 #[必须]服务器唯一ID
重启Mysql服务
登录Mysql数据库,设置主库地址及同步位置:change master to master_host=‘主库IP’,master_user=‘主库远程访问名字’,master_password=‘主库远程访问密码’,master_log_file=‘二进制文件名’,master_log_pos=主库查出来的;
在进行环境配置容易出现的问题
1.修改密码报错(8版本):
原因:由于mysql的默认密码政策是medium,应该修改为low
方法:SHOW VARIABLES LIKE ‘validate_password%’;set global validate_password.policy=LOW;
2. 熊猫脚连不上mysql(8版本):
原因:mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是caching_sha2_password
方法:参考 https://www.freesion.com/article/59151342907/
3. 主从库配置中I/O流线程起动失败,日志报uuid相同:
原因:这是由于服务器是克隆过来的,mysql的uuid配置肯定相同
方法:在/var/lib/mysql/auto.cnf有一个auto.cnf文件,修改主从库任意一个的uuid(可以通过在数据库中执行select uuid():得到一个uuid)

2.2 读写分离的案例

2.2.1 背景介绍

		面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为**主库**和**从库**,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。通过读写分离,就可以降低单台数据库的访问压力, 提高访问效率,也可以避免单机故障。

2.2.2 关于ShardingJDBC

	Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。使用Sharding-JDBC可以在程序中轻松的实现数据库读写分离。
Sharding-JDBC具有以下几个特点: 
	1). 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
	2). 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
	3). 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

2.2.2.1 使用步骤

引入依赖

<dependency>
    <groupId>org.apache.shardingspheregroupId>
    <artifactId>sharding-jdbc-spring-boot-starterartifactId>
    <version>4.0.0-RC1version>
dependency>

application.yml中增加数据源的配置

spring:
  shardingsphere:
    datasource:
      names:
        master,slave
      # 主数据源
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.200.200:3306/rw?characterEncoding=utf-8
        username: root
        password: root
      # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.200.201:3306/rw?characterEncoding=utf-8
        username: root
        password: root
    masterslave:
      # 读写分离配置
      load-balance-algorithm-type: round_robin #轮询
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: slave
    props:
      sql:
        show: true #开启SQL显示,默认false
    main:
      allow-bean-definition-overriding: true

2.3 读写分离引入项目

步骤:导入依赖 + 编写配置文件

三 关于nginx部分

3.1 关于nginx常用命令

  1. 查看版本号 ./nginx -v
  2. 检查配置文件的正确性: ./nginx -t
  3. 启动与停止:1)启动:./nginx 关闭防火墙systemctl stop fireword 2)停止:./nginx -s stop
  4. 重新加载配置文件 :./nginx -s reload
    简化写话:将二进制文件放到etc/profile;source /etc/profile

3.2 重点目录和文件如下

关与一个SpringBoot单体项目的优化-->外卖平台优化_第4张图片

3.3 负载均衡策略

关与一个SpringBoot单体项目的优化-->外卖平台优化_第5张图片
#upstream指令可以定义一组服务器

四 前后端分离优化

何为前后端分离:拆分为:前端工程(nginx)+后端工程 (tomcat )

4.1 开发流程

关与一个SpringBoot单体项目的优化-->外卖平台优化_第6张图片

4.2 关于一个接口依赖swagger

4.2.1 介绍

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。功能主要包含以下几点:
A. 使得前后端分离开发更加方便,有利于团队协作
B. 接口文档在线自动生成,降低后端开发人员编写接口文档的负担
C. 接口功能测试
使用Swagger只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就
可以做到生成各种格式的接口文档,以及在线接口调试页面等等。

直接使用Swagger, 需要按照Swagger的规范定义接口, 实际上就是编写Json文件,编写起来比较繁琐、并不方便, 。而在项目中使用,我们一般会选择一些现成的框架来简化文档的编写,而这些框架是基于Swagger的,如knife4j。knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。而我们要使用kinfe4j,需要在pom.xml中引入如下依赖即可:

<dependency>
    <groupId>com.github.xiaoymingroupId>
    <artifactId>knife4j-spring-boot-starterartifactId>
    <version>3.0.2version>
dependency>

4.2.1 使用方式

1). 导入knife4j的maven坐标

<dependency>
    <groupId>com.github.xiaoymingroupId>
    <artifactId>knife4j-spring-boot-starterartifactId>
    <version>3.0.2version>
dependency>

2). 导入knife4j相关配置类
这里我们就不需要再创建一个新的配置类了,我们直接在WebMvcConfig配置类中声明即可。
A. 在该配置类中加上两个注解 @EnableSwagger2 @EnableKnife4j ,开启Swagger和Knife4j的功能。
B. 在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的信息。

 @Bean
    public Docket createRestApi() {
        // 文档类型
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller"))
                .paths(PathSelectors.any())
                .build();
    }
	
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("瑞吉外卖")
                .version("1.0")
                .description("瑞吉外卖接口文档")
                .build();
    }

3). 设置静态 映射

registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

4). 在LoginCheckFilter中设置不需要处理的请求路径
需要将Swagger及Knife4j相关的静态资源直接放行,无需登录即可访问,否则我们就需要登录之后,才可以访问接口文档的页面。

"/doc.html",
"/webjars/**",
"/swagger-resources",
"/v2/api-docs"

4.2.2 常用注解

关与一个SpringBoot单体项目的优化-->外卖平台优化_第7张图片

你可能感兴趣的:(springboot,spring,boot,缓存,redis)