• 关与一个SpringBoot单体项目的优化-->瑞吉外卖优化


    一 缓存优化

    分析:在原先的代码中,我们将短信验证码保存在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;
    
    • 1
    • 2

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

    1.1.3 清理菜品缓存

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

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

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

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

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

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

    关于SpringCache

    1.2.1 介绍

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

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

    针对不同的缓存技术需要实现不同的CacheManager:
    请添加图片描述

    1.2.1 常用注解

    请添加图片描述
    在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>
    
    • 1
    • 2
    • 3
    • 4
    2). 在application.yml中配置缓存数据的过期时间
    
    • 1
    spring:  
      cache:
        redis:
          time-to-live: 1800000 #设置缓存数据的过期时间
    
    • 1
    • 2
    • 3
    • 4
    3). 在启动类上加入@EnableCaching注解,开启缓存注解功能
    4). 在SetmealController的list方法(查询方法)上加入@Cacheable注解
    5). 在SetmealController的save和delete方法上加入CacheEvict注解
    
    • 1
    • 2
    • 3

    二 读写分离优化

    2.1 读写分离概念

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

    2.1.1 数据库主从复制

    2.1.1.1 介绍

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

    2.1.1.2 复制原理

    请添加图片描述
    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 背景介绍

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

    2.2.2.1 使用步骤

    引入依赖

    <dependency>
        <groupId>org.apache.shardingspheregroupId>
        <artifactId>sharding-jdbc-spring-boot-starterartifactId>
        <version>4.0.0-RC1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    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 重点目录和文件如下

    请添加图片描述

    3.3 负载均衡策略

    请添加图片描述
    #upstream指令可以定义一组服务器

    四 前后端分离优化

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

    4.1 开发流程

    请添加图片描述

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2.1 使用方式

    1). 导入knife4j的maven坐标

    <dependency>
        <groupId>com.github.xiaoymingroupId>
        <artifactId>knife4j-spring-boot-starterartifactId>
        <version>3.0.2version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3). 设置静态 映射

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

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

    "/doc.html",
    "/webjars/**",
    "/swagger-resources",
    "/v2/api-docs"
    
    • 1
    • 2
    • 3
    • 4

    4.2.2 常用注解

    请添加图片描述

  • 相关阅读:
    ibstdc++.so.6: version `GLIBCXX_3.4.30‘ not found (
    Linux常用命令—find命令大全
    自动化横行的今天,手工测试如何杀出一条血路?
    顾客:花钱还要受气?消费体验令人下头,根因与解药何在?
    中国软件三季度业绩预测,中国软件股票趋势预测
    java Web的基本介绍
    用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)
    Java中的数组
    30+下岗程序员专职奶爸如何生存?
    springboot源码理解二、依赖管理
  • 原文地址:https://blog.csdn.net/ljbnb/article/details/126017274