瑞吉外卖项目--项目优化

目录

  • 源码获取
  • 前言
  • 十五、版本控制
    • 15.1 创建远程仓库
    • 15.2 初始化本地仓库
    • 15.3 关联远程仓库并推送到远程仓库
    • 15.4 打标签(v0 即未优化版本)
    • 15.5 创建分支dev1
  • 十六、redis缓存
    • 16.1、环境搭建
      • 16.1.1 导入redis依赖
      • 16.1.2 配置文件
    • 16.2、缓存短信验证码
      • 16.2.1 实现思路
      • 16.2.2 代码
    • 16.3、缓存菜品数据
      • 16.3.1 实现思路
      • 16.3.2 代码
        • 16.3.2.1 list方法
        • 16.3.2.2 update方法
        • 16.3.3.3 save方法
    • 16.4、Spring Cache
      • 16.4.1 Spring Cache介绍
      • 16.4.2 常用注解
      • 16.4.3 基本使用
        • 19.4.3.1 依赖
        • 16.4.3.2 @CachePut
        • 14.4.3.3 @CacheEvict
        • 16.4.3.3 @Cacheable
    • 16.5、以redis作为缓存
      • 16.5.1 依赖
      • 16.5.2 application.yml 配置
    • 16.6、缓存套餐数据
      • 16.6.1 实现思路
      • 16.6.2 代码
        • 16.6.2.1 导入依赖
        • 16.6.2.2 设置缓存数据过期时间
        • 16.6.2.3 开启缓存
        • 16.6.2.4 R类序列化
        • 16.6.2.5 list方法
        • 16.6.2.6 delete 和save方法
  • 十七、mysql主从复制
    • 17.1 Mysql实现主从复制
      • 17.1.1 基本原理介绍
      • 17.1.2 配置主从数据库
    • 17.2 读写分离案例
      • 17.2.1 背景
      • 17.2.2 SHarding-JDBC
    • 17.3 项目实战
  • 十八、Nginx
    • 18.1 介绍
    • 18.2 下载和安装
    • 18.3 nginx目录结构
    • 18.4 nginx命令
      • 18.4.1 查看版本
      • 18.4.2 检查配置文件正确性
      • 18.4.3 启动和停止nginx
      • 18.4.4 重新加载配置文件
      • 18.4.5 修改/etc/profile,环境变量
    • 18.5 nginx配置文件结构
    • 18.6 nginx具体应用
      • 18.6.1 部署静态资源
      • 18.6.2 反向代理
      • 18.6.3 负载均衡
  • 十九 前后端分离开发
    • 19.1 介绍
    • 19.2 开发流程
    • 19.3 Swagger
      • 19.31 介绍
      • 19.3.2 使用
        • 19.3.2.1 导入依赖
        • 19.3.2.2 导入knife4j配置
        • 19.3.2.3 设置静态资源映射
        • 19.3.2.4 设置过滤器放行路径
        • 19.3.2.5 测试
      • 19.3.3 常用注解
  • 二十 项目部署
    • 20.1 部署架构
    • 20.2 部署环境
    • 20.3 部署前端项目
    • 20.4 部署后端项目

源码获取

reggie项目优化源码gitee地址(优化的代码在标签v1、v2、v3):https://gitee.com/gaoqiangmath/reggie.git

前言

本博客是瑞吉外卖项目–服务端开发以及瑞吉外卖项目–移动端开发的后续
瑞吉外卖项目–服务端开发的博客见:瑞吉外卖项目–服务端开发
瑞吉外卖项目–移动端开发的博客见:瑞吉外卖项目–移动端开发

本博客是根据b站瑞吉外卖视频(p157-p191)整理而来
视频链接:https://www.bilibili.com/video/BV13a411q753/?p=2&spm_id_from=pageDriver&vd_source=f4a032fee75744e378f4ac30c7e8ad39

十五、版本控制

15.1 创建远程仓库

瑞吉外卖项目--项目优化_第1张图片(注意:要先清空仓库)

15.2 初始化本地仓库

瑞吉外卖项目--项目优化_第2张图片

15.3 关联远程仓库并推送到远程仓库

瑞吉外卖项目--项目优化_第3张图片
瑞吉外卖项目--项目优化_第4张图片瑞吉外卖项目--项目优化_第5张图片

15.4 打标签(v0 即未优化版本)

打标签:

在这里插入图片描述
推送标签:

在这里插入图片描述

关于标签需要知道的操作:

  • 查看标签:git tag
  • 打标签:git tag -a 标签名 -m "标签备注"
  • 删除远地标签:git push origin :refs/tags/标签名
  • 删除本地标签:git tag -d 标签名
  • 切换到标签对应版本(注意,切换到标签需要创建一个分支):git checkout -b 分支名(自己取) 标签名

15.5 创建分支dev1

后续代码优化都在此分支上

创建本地分支dev1(主分支的基础上在):

在这里插入图片描述
将本地分支dev1推送到远程:

在这里插入图片描述

十六、redis缓存

redis缓存优化在分支dev1 ,标签v1

16.1、环境搭建

16.1.1 导入redis依赖

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

hutool工具

<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.8.15version>
dependency>

16.1.2 配置文件

application.yml

spring:
  redis:
    host: 192.168.10.103
    port: 6379
    password: 123456

16.2、缓存短信验证码

16.2.1 实现思路

  • 之前所实现的业务功能是这样的:随机生成的验证码保存在HttpSession中,现在需要改造为将验证码缓存在Redis中,实现思路如下:
  • 在服务端UserController注入StringRedisTemplate对象,用于操作redis
  • 在服务端UserController的sendMsg方法,将随机生成的验证码缓存到Redis中,设置有效期为5分钟
  • 在服务端UserController的login方法中,从Redis中获取缓存的验证码,如果登录成功则删除Redis中的验证码

16.2.2 代码

package com.gq.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gq.reggie.common.R;
import com.gq.reggie.entity.User;
import com.gq.reggie.service.UserService;
import com.gq.reggie.utils.SMSUtils;
import com.gq.reggie.utils.ValidateCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发送手机短信验证码
     */
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession session) {
        //获取手机号
        String phone = user.getPhone();
        if (StringUtils.isNotEmpty(phone)) {
            //随机生成的4为验证码
            Integer integerCode = ValidateCodeUtils.generateValidateCode(4);
            String code = integerCode.toString();

            //这是模拟的,验证码发到控制台
            log.info("手机验证码={}", code);

            //调用阿里云提供的短信服务api完成发送短信  这里仅作学习使用,不需要真正发送验证码,所以关掉了
            //参数分别是:阿里云创建的标签名、模板code、手机号码、验证码
            SMSUtils.sendMessage("瑞吉外卖", "SMS_460655856", phone, code);

            log.info("阿里云手机验证码发送成功");

            //把验证码保存到redis中,并且设置为5分钟期限
            //session.setAttribute(phone, code);
            stringRedisTemplate.opsForValue().set(phone, code, 5L, TimeUnit.MINUTES);

            return R.success("手机验证码发送成功");
        }

        return R.error("手机验证码发送失败");
    }


    /**
     * 移动端用户登录
     */
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session) {
        //log.info(map.toString());

        //获取手机号
        String phone = map.get("phone").toString();

        //获取验证码
        String code = map.get("code").toString();

        //从redis中获取保存的验证码
        String codeInSession = stringRedisTemplate.opsForValue().get(phone);

        //进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
        if (codeInSession != null && codeInSession.equals(code)) {
            //如果能够比对成功,说明登录成功

            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
            //根据用户的手机号去用户表获取用户
            User user = userService.getOne(queryWrapper);
            if (user == null) {
                //判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
                user = new User();
                user.setPhone(phone);
                user.setStatus(1); //可设置也可不设置,因为数据库我们设置了默认值
                //注册新用户
                userService.save(user);
            }
            //这一行容易漏。。保存用户登录状态
            session.setAttribute("user", user.getId()); //在session中保存用户的登录状态,这样才过滤器的时候就不会被拦截了
            return R.success(user);
        }

        //如果登陆成功,则在redis中删除验证码
        if (stringRedisTemplate.opsForValue().get(phone) != null) {
            stringRedisTemplate.delete(phone);
        }
        return R.error("登录失败");
    }

}

16.3、缓存菜品数据

16.3.1 实现思路

瑞吉外卖项目--项目优化_第6张图片这里删除的方法也要请缓存

16.3.2 代码

16.3.2.1 list方法

DishController中的list方法

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/list")
    public R<List<DishDto>> list(Dish dish) { //会自动映射的
        //这里可以传categoryId,但是为了代码通用性更强,这里直接使用dish类来接受(因为dish里面是有categoryId的),以后传dish的其他属性这里也可以使用

        //从redis中拿菜品
        // 动态构造key
        String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus();
        String jsonDishs = stringRedisTemplate.opsForValue().get(key);
        List<DishDto> dishDtoList = JSONUtil.toList(jsonDishs, DishDto.class);


        //构造查询条件
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        //添加条件,查询状态为1(起售状态)的菜品
        queryWrapper.eq(Dish::getStatus, 1);

        //添加排序条件
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(queryWrapper);

        //进行集合的泛型转化
        dishDtoList = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            //为一个新的对象赋值,一定要考虑你为它赋过几个值,否则你自己都不知道就返回了null的数据
            //为dishDto对象的基本属性拷贝
            BeanUtils.copyProperties(item, dishDto);
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            if (category != null) {
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }
            //为dishdto赋值flavors属性
            //当前菜品的id
            Long dishId = item.getId();
            //创建条件查询对象
            LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper();
            lambdaQueryWrapper.eq(DishFlavor::getDishId, dishId);
            //select * from dish_flavor where dish_id = ?
            //这里之所以使用list来条件查询那是因为同一个dish_id 可以查出不同的口味出来,就是查询的结果不止一个
            List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
            dishDto.setFlavors(dishFlavorList);

            return dishDto;
        }).collect(Collectors.toList());
        //将dishDtoList存入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(dishDtoList));

        return R.success(dishDtoList);
    }

16.3.2.2 update方法

    /**
     * 修改菜品以及口味信息
     *
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto) {
        dishService.updateWithFlavor(dishDto);
        
        //清理修改的菜品的缓存数据
        String key = "dish_" + dishDto.getCategoryId() + "_" + dishDto.getStatus();
        stringRedisTemplate.delete(key);
        
        return R.success("菜品修改成功");
    }

16.3.3.3 save方法

    /**
     * 新增菜品
     *
     */
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto) {//前端提交的是json数据的话,我们在后端就要使用这个注解来接收参数,否则接收到的数据全是null
        dishService.saveWhthFlavor(dishDto);

        //清理修改的菜品的缓存数据
        String key = "dish_" + dishDto.getCategoryId() + "_" + dishDto.getStatus();
        stringRedisTemplate.delete(key);
        
        return R.success("新增菜品成功");
    }

16.4、Spring Cache

16.4.1 Spring Cache介绍

瑞吉外卖项目--项目优化_第7张图片

16.4.2 常用注解

瑞吉外卖项目--项目优化_第8张图片

16.4.3 基本使用

19.4.3.1 依赖

Spring Cache 的依赖在 spring-boot-starter-web

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

16.4.3.2 @CachePut

    /**
     * CachePut:将方法返回值放入缓存
     *      value:缓存的名称,每个缓存下可以有多个key
     *      key:缓存的key
     * @param user
     * @return
     */
    @CachePut(value = "userCache", key = "#user.id")
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

14.4.3.3 @CacheEvict

    /**
     * CacheEvict:清理指定缓存,key有多种写法
     * @param id
     */
    @CacheEvict(value = "userCache", key = "#id")
    //@CacheEvict(value = "userCache", key = "#root.args[0]")
    //@CacheEvict(value = "userCache", key = "#p0")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }
 
    @CacheEvict(value = "userCache", key = "#p0.id")
    //@CacheEvict(value = "userCache", key = "#root.args[0].id")
    //@CacheEvict(value = "userCache", key = "#user.id")
    //@CacheEvict(value = "userCache", key = "#result.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

16.4.3.3 @Cacheable

    /**
     * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
     *           若数据库没有找到数据,以null为key
     *      unless:缓存条件,满足条件才缓存
     * @param id
     * @return
     */
    @Cacheable(value = "userCache", key = "#id", unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

16.5、以redis作为缓存

16.5.1 依赖

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-cacheartifactId>
        dependency>
 
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

在这里插入图片描述

16.5.2 application.yml 配置

spring:
  redis:
    host: 192.168.10.103
    port: 6379
    password: 123456
  cache:
    redis:
      time-to-live: 1800000 # 设置缓存有效时间,以毫秒为单位

16.6、缓存套餐数据

16.6.1 实现思路

瑞吉外卖项目--项目优化_第9张图片

16.6.2 代码

16.6.2.1 导入依赖

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
 
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-cacheartifactId>
        dependency>

16.6.2.2 设置缓存数据过期时间

application.yml

spring:
  cache:
    redis:
      time-to-live: 1800000 # 设置缓存有效时间,以毫秒为单位

16.6.2.3 开启缓存

启动类上加上注解@EnableCaching

@EnableCaching  // 开启Spring Cache缓存

16.6.2.4 R类序列化

common包下R

public class R<T> implements Serializable

16.6.2.5 list方法

SetmealControllerlist

    /**
     * 移动端套餐展示
     *
     * @param categoryId
     * @param status
     * @return
     */
    @GetMapping("/list")
    @Cacheable(value = "setmealCache",key = "#categoryId + '_' + #status")
    public R<List<Setmeal>> listSetmeal(@RequestParam(value = "categoryId") long categoryId, @RequestParam(name = "status") int status) {
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, categoryId);
        setmealLambdaQueryWrapper.eq(Setmeal::getStatus, status);
        setmealLambdaQueryWrapper.orderByAsc(Setmeal::getCreateTime);
        List<Setmeal> setmealList = setmealService.list(setmealLambdaQueryWrapper);
        return R.success(setmealList);

    }

16.6.2.6 delete 和save方法

SetmealController中:

    /**
     * 套餐删除
     * allEntries:清理value分类下所有缓存数据
     * @param ids
     * @return
     */
    @DeleteMapping
    @CacheEvict(value = "setmealCache",allEntries = true)
    public R<String> deleteByIds(@RequestParam(name = "ids") List<Long> ids) {
        setmealService.removeWithDish(ids);
        return R.success("套餐删除成功!!");
    }
    /**
     * 套餐管理模块中:保存新增的套餐
     * allEntries:清理value分类下所有缓存数据
     * @param setmealDto
     * @return
     */
    @PostMapping
    @CacheEvict(value = "setmealCache",allEntries = true)
    public R<String> saveSetmeal(@RequestBody SetmealDto setmealDto) {
        setmealService.saveSetmeal(setmealDto);
        return R.success("套餐添加成功!!");
    }

十七、mysql主从复制

redis缓存以及mysql主从复制在分支dev2 标签v2

17.1 Mysql实现主从复制

17.1.1 基本原理介绍

瑞吉外卖项目--项目优化_第10张图片
瑞吉外卖项目--项目优化_第11张图片

17.1.2 配置主从数据库

第一:准备两台服务器,分别安装了mysql8
主服务器:hadoop103 192.168.10.103
从服务器:hadoop103salve 192.168.10.104

注意:从服务器mysql数据库里面的内容不要和主服务器mysql服务器的内容冲突,最好都是两个空的,等搭建好了主从结构,再从主服务器添加数据

一定要注意:从服务器是直接克隆的主服务器,一定要修改如下三个地方

  • 第一:一定要求改从虚拟机ip地址uuid
cat /etc/sysconfig/network-scripts/ifcfg-ens33 
  • 第二:一定要求改从虚拟机hostname
sudo vi /etc/hostname

更新/etc/hosts文件 【更改主机名后】
在更改主机名后我们需要更新/etc/hosts解析文件。

sudo vi /etc/hosts

瑞吉外卖项目--项目优化_第12张图片

  • 第三:(如果安装有mysql)一定要求改从虚拟机mysql中的uuid

首先找到auto.cnf文件

find / -iname auto.cnf

在这里插入图片描述
其次,修改里面的uuid

vim /usr/local/mysql-8.0.31/data/auto.cnf #注意,这里是自己的auto.cnf文件位置

配置主库:hadoop103 192.168.10.103

  • 第一步:修改Myusql数据库的配置文件/etc/my.cnf
[mysqld]
log-bin=mysql-bin #[必须]启用二进制日志
server-id=103 #[必须]服务器唯一ID,只需要确保其id是唯一的就好
  • 第二步:重启Mysql服务
systemctl restart mysql
  • 第三步:登录Mysql数据库,执行下面的SQL

mysql版本在8以及以上的,使用这个:

create user gaoqiang identified by 'Root@123456';
grant replication slave on *.* to gaoqiang;

mysql版本在8以下的,使用这个:

grant replication slave on *.* to 'gaoqiang'@'%' identified by 'Root@123456';

上面的SQL的作用是创建一个用户gaoqiang,密码为Root@123456,并且给gaoqiang用户授予replication slave权限,常用语建立复制时所需要用到的用户权限,也就是slave必须被master授权具有该权限的用户,才能通过该用户复制,这是因为主库和从库之间需要互相通信,处于安全考虑,只有通过验证的从库才能从主库中读取二进制数据。

  • 第四步:登录Mysql数据库,执行下面的SQL,记录下结果中File和Position的值
show master status;

瑞吉外卖项目--项目优化_第13张图片

上面指令的作用是查看Master的状态,执行完此SQL后不要在执行任何操作,如果执行了别的操作的话会使得File和position发生变化

配置从库:hadoop103salve 192.168.10.104

  • 第一步:(这一步和配置主库的是一致的)]修改Mysql数据库的配置文件/etc/my.cnf
[mysqld]
log-bin=mysql-bin #[必须]启用二进制日志
server-id=104 #[必须]服务器唯一ID,只需要确保其id是唯一的就好
  • 第二步:重启Mysql服务
systemctl restart mysql
  • 第三步:登录Mysql数据库,执行下面的SQL,参数根据之前的设置(配置主服务器的第四步)灵活调整即可
change master to master_host='192.168.10.103',master_user='gaoqiang',master_password='Root@123456',master_log_file='mysql-bin.000004',master_log_pos=697,master_connect_retry=60, GET_MASTER_PUBLIC_KEY=1;
start slave;
  • 第四步:登录Mysql数据库,执行SQL,查看从库的状态
show slave status \G;

瑞吉外卖项目--项目优化_第14张图片

(到这里就搭建好了主从数据库)

17.2 读写分离案例

17.2.1 背景

面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库主要负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大改善

瑞吉外卖项目--项目优化_第15张图片

17.2.2 SHarding-JDBC

瑞吉外卖项目--项目优化_第16张图片


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

17.3 项目实战

  • 步骤一:搭建好主从复制结构

主从复制结构见17.1.2 配置主从数据库
并且在主库中创建reggie数据库,导入数据(具体的sql文件见:瑞吉外卖项目–服务端开发中的3.1、数据库环境搭建)

  • 步骤二:加入maven坐标

<dependency>
    <groupId>org.apache.shardingspheregroupId>
    <artifactId>sharding-jdbc-spring-boot-starterartifactId>
    <version>4.0.0-RC1version>
dependency>
  • 步骤三:修改application.yml配置文件

(注意在配置文件中配置允许bean定义覆盖配置项)

原本的application.yml配置文件:

server:
  port: 8080
spring:
  application:
    # 应用的名称,选择性配置
    name: reggie_take_out
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: 123456
  redis:
    host: 192.168.10.103
    port: 6379
    password: 123456
  cache:
    redis:
      time-to-live: 1800000 # 设置缓存有效时间,以毫秒为单位

mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    # 把SQL的查询的过程输出到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      #主键的生成策略,雪花算法
      id-type: ASSIGN_ID


#文件储存位置
reggie:
  path: D:\Study\Items\reggie\pictures\

添加了主从复制的application.yml配置文件

server:
  port: 8080

spring:
  application:
    # 应用的名称,选择性配置
    name: reggie_take_out
  main:
    allow-bean-definition-overriding: true   #允许bean定义覆盖配置项

#  datasource:
#    druid:
#      driver-class-name: com.mysql.cj.jdbc.Driver
#      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
#      username: root
#      password: 123456

  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.10.103:3306/reggie?characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
      # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.10.104:3306/reggie?characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    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

  redis:
    host: 192.168.10.103
    port: 6379
    password: 123456
  cache:
    redis:
      time-to-live: 1800000 # 设置缓存有效时间,以毫秒为单位

mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    # 把SQL的查询的过程输出到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      #主键的生成策略,雪花算法
      id-type: ASSIGN_ID


#文件储存位置
reggie:
  path: D:\Study\Items\reggie\pictures\

步骤四:测试

重启项目,发现启动了两个数据源

瑞吉外卖项目--项目优化_第17张图片

(查询操作走的是slave从库)

瑞吉外卖项目--项目优化_第18张图片

(修改走的是master主库)

瑞吉外卖项目--项目优化_第19张图片

十八、Nginx

18.1 介绍

瑞吉外卖项目--项目优化_第20张图片

18.2 下载和安装

Windows 下载地址:http://nginx.org/,选择需要的版本下载即可

瑞吉外卖项目--项目优化_第21张图片

(本处使用的是linux版本)

安装依赖包:
yum -y install gcc pcre-devel zlib-devel openssl openssl-devel
 
安装wget   以便于可以直接通过网址下载软件
yum install wget
 
下载nginx安装包 建议在/usr/local目录下下载(解压完后,把安装包删掉)
wget http://nginx.org/download/nginx-1.24.0.tar.gz
 
解压
tar -zxvf nginx-1.24.0.tar.gz -C /usr/local
 
进入根目录
cd nginx-1.24.0/
 
创建安装目录
mkdir -p /usr/local/nginx
 
检查nginx安装环境(在nginx-1.24.0/内输入下述命令)
./configure --prefix=/usr/local/nginx
 
编译并安装nginx,会安装到/usr/local/nginx中 (在nginx-1.24.0/内输入下述命令)
make && make install

18.3 nginx目录结构

为了方便看目录,可以下载一个tree

yum install tree

瑞吉外卖项目--项目优化_第22张图片
瑞吉外卖项目--项目优化_第23张图片

18.4 nginx命令

18.4.1 查看版本

在进入 nginx 目录下的 sbin 目录后,使用以下命令可以查看 nginx 版本

./nginx -v

18.4.2 检查配置文件正确性

在启动 Nginx 服务之前,可以先检查一下 conf/nginx.conf 文件配置的是否有错误,进入 nginx 目录下的 sbin 目录后执行如下命令:

./nginx -t

在这里插入图片描述

18.4.3 启动和停止nginx

启动:进入 nginx 目录下的 sbin 目录后执行如下命令:

./nginx

记得关闭防火墙:

systemctl stop friewalld

启动后可以查看 nginx 进程

ps -ef | grep nginx

停止:进入 nginx 目录下的 sbin 目录后执行如下命令:

./nginx -s stop

18.4.4 重新加载配置文件

当修改Nginx配置文件后,需要重新加载才能生效,可以使用下面命令重新加载配置文件:
进入 nginx 目录下的 sbin 目录后执行如下命令:

./nginx -s reload

18.4.5 修改/etc/profile,环境变量

修改 /etc/profile 文件

在 PATH 前加上 nginx 的 sbin 的完整路径,这里是 /usr/local/nginx/sbin: (注意有个冒号)

PATH=/usr/local/nginx/sbin:$PATH

修改完后重新加载

source /etc/profile

改完之后,可以任意位置使用nginx命令

18.5 nginx配置文件结构

瑞吉外卖项目--项目优化_第24张图片

18.6 nginx具体应用

18.6.1 部署静态资源

瑞吉外卖项目--项目优化_第25张图片

18.6.2 反向代理

瑞吉外卖项目--项目优化_第26张图片

瑞吉外卖项目--项目优化_第27张图片

18.6.3 负载均衡

瑞吉外卖项目--项目优化_第28张图片
瑞吉外卖项目--项目优化_第29张图片
瑞吉外卖项目--项目优化_第30张图片

瑞吉外卖项目--项目优化_第31张图片

十九 前后端分离开发

19.1 介绍

瑞吉外卖项目--项目优化_第32张图片

19.2 开发流程

瑞吉外卖项目--项目优化_第33张图片

19.3 Swagger

(此功能在dev3分支上:redis缓存、mysql主从复制、swagger)

19.31 介绍

瑞吉外卖项目--项目优化_第34张图片

19.3.2 使用

瑞吉外卖项目--项目优化_第35张图片

19.3.2.1 导入依赖

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

19.3.2.2 导入knife4j配置

WebMvcConfig 类上加上两个注解

@EnableSwagger2
@EnableKnife4j

然后在WebMvcConfig类中添加这两个方法

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

19.3.2.3 设置静态资源映射

WebMvcConfig 类的 addResourceHandlers 配置静态资源映射

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

最终版:WebMvcConfig

package com.gq.reggie.config;

import com.gq.reggie.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;


@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始进行静态资源映射");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

    }


    /**
     * 扩展mvc框架的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //log.info("扩展消息转换器...");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        //转换器是有优先级顺序的,这里我们把自己定义的消息转换器设置为第一优先级,所以会优先使用我们的转换器来进行相关数据进行转换,如果我们的转换器没有匹配到相应的数据来转换,那么就会去寻找第二个优先级的转换器,以此类推
        converters.add(0,messageConverter);
    }

    @Bean
    public Docket createRestApi() {
        // 文档类型
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.gq.reggie.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("瑞吉外卖")
                .version("1.0")
                .description("瑞吉外卖接口文档")
                .build();
    }


}

19.3.2.4 设置过滤器放行路径

LoginCheckFilter 类的 doFilter 方法添加放行路径(为了方便)

                // knife4j 放行路径
                "/doc.html",
                "/webjars/**",
                "/swagger-resources",
                "/v2/api-docs"

19.3.2.5 测试

在浏览器访问 http://localhost:8080/doc.html ,可以看到接口文档

瑞吉外卖项目--项目优化_第36张图片

19.3.3 常用注解

瑞吉外卖项目--项目优化_第37张图片

二十 项目部署

20.1 部署架构

(以红字为准)

瑞吉外卖项目--项目优化_第38张图片

20.2 部署环境

准备两台机器:
192.168.10.103 : 主从复制的主库,需要的软件:jdkmysql8nginxredis (如果手动把jar包传入,不需要git maven)
192.168.10.104:主从复制的从库,需要的软件:mysql8

20.3 部署前端项目

1、将前端资料打包,传到 nginx 根目录下的 html 目录
2、修改 nginx.conf

    server{
        listen 80;
        server_name localhost;
 
        location / {
            root html/dist;     # 根目录
            index index.html;   # 默认首页
        }
 
        # 反向代理
        location ^~ /api/ {
            rewrite ^/api/(.*)$ /$1 break;      # rewirte:重写url,将 /api/ 除去
            proxy_pass proxy_pass http://192.168.10.103:8080;
        }
 
        error_page 500 502 503 504 /50x.html;
        location = /50x.html{
            root html;
        }
    }

20.4 部署后端项目

(建议使用docker,这里这样不推荐)
将打包好的jar包传入到服务器:192.168.10.103
传入到:/usr/local/app/目录下,后台运行

nohup java -jar reggie-0.0.1-SNAPSHOT.jar >server.log 2>&1 &

你可能感兴趣的:(java项目,git,github,java,tomcat,redis)