11.3 优化前git linux Redis

1.头:日期、所学内容出处

【黑马程序员2022新版SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术】 https://www.bilibili.com/video/BV1Fi4y1S7ix?p=31&share_source=copy_web&vd_source=c8ae4150b2286ee39a13a79bbe12b843

2.所学内容概述

Git命令

git在之前学过一遍,做博客的时候也应用过,已经很熟悉了。记下命令吧

本地仓库的操作

命令 作用
git status 查看文件状态
git add 将文件修改加入暂存区
git reset 将暂存区的文件取消暂存或者切换版本
git commit 将暂存区的文件修改提交到版本库
git log 查看日志

远程仓库操作

命令 作用
git remote 查看远程仓库
git remote add 添加远程仓库
git clone 从远程仓库克隆
git pull 从远程仓库拉取
git push 推送到远程仓库

标签操作 可以切换版本和更新时间

命令 作用
git tag 列出已有的标签
git tag [name] 创建标签
git push [shortName] [name] 将标签推送远程仓库
git checkout -b [branch] [name] 检出标签

在IDEA中集成使用Git

==在上面VCS中,点commit 选git 然后把路径改成自己git.exe安装的路径 就完成了IDEA集成,设置和远程仓库,就能用了==

  1. 本地初始仓库
    • 选择VCS选项卡 —> 创建Git仓库 —> 选择需要被Git管理的目录 —> 确定
  2. 从远程仓库克隆(常用)
    • 这个可以自己选择克隆的本地位置
    • 可以直接把远程仓库的代码都克隆到本地
    • 远程克隆下来的项目会自带一个文件:.gitignore文件,在里面的信息是代表哪些文件不需要交给git管理

Linux

比较简单和基础,过一遍发现是自己都会的,简单的安装软件配置环境,还是很easy的

Redis

跟着视频把Redis安装完成了

Linux安装Redis

  1. 将Redis安装包上传Linux

  2. 解压安装包,改成你自己的redis版本,路径解压到/usr/local

    1
    tar -zxvf redisVersion.tar.gz -C /usr/local
  3. 安装Redis的依赖环境gcc,编译

    1
    2
    # 安装依赖环境
    yum install gcc-c++
  4. 进入redis,编译

    1
    2
    3
    4
    5
    # 进入到根目录
    cd /usr/local/redis根目录

    # 编译
    make
  5. 进入redis的src目录,进行安装

    1
    2
    3
    4
    # 进入到src目录
    cd /usr/local/redis根目录/src
    # 进行安装
    make install

Redis启动和停止

设置一个后台运行,需要先修改redis.conf文件把aemonize on字段,将其修改为daemonize yes

1
2
# 运行server 选择配置文件 后台运行
src/redis-server ./redis.conf

Redis添加密码登陆

还是修改redis.conf配置文件,找到requirepass这行,将其注释去掉,并在后面写上自己的密码

1
2
3
4
5
6
# 重新启动
src/redis-server ./redis.conf

# 登录时同时进行认证
src/redis-cli -h localhost -p 6379 -a 密码
# 需要再次

3. BUG点

难点(关键代码或关键配置,BUG截图+解决方案)

IDEA集成Git,一直报错,自己另外提交了一个就没问题,不知道什么原因。
CSDN搜到原因了,是文件在本地仓库和远程仓库上不一致,但是不知道如何解决,看两个库中唯一一个共同的文件是.gitnore打开,的确是不一样,就干脆直接把远程仓库的.gitnore删除了,再次提交,成功了

img

4.扩展学习部分

5.总结

今天的学习其实主要是复习和巩固,之前粗略学习的git和redis,全部重新过了一下,难度不大,就是晚上想把瑞吉上传到码云的时候,出了些问题,搞了有点久了,在前面的基本没什么问题,分支那边懂是懂了,但是没有实际操作过,因为自己做项目就一个分支就好了。明天试着把瑞吉部署到服务器中,就彻底结束SpringBotot的学习了。

11.4 瑞吉外卖优化

1.头:日期、所学内容出处

【黑马程序员2022新版SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术】 https://www.bilibili.com/video/BV1Fi4y1S7ix?p=31&share_source=copy_web&vd_source=c8ae4150b2286ee39a13a79bbe12b843

2.所学内容概述

Git部署

之前git已经集成IDEA成功,直接commit完了直接提交没有报错,很顺利也很简单。

缓存优化

缓存优化用Redis,导入SpringDataRedis坐标

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

修改yml配置文件

1
2
3
4
5
redis:
host: 172.16.1.76
port: 6379
password: 123456
database: 0

配置序列化器

方便图形化界面中看我们存入的数据

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//默认key序列化器为:JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}

代码改造

因为实现的思路比较长,就写代码注释中了,这里使用一个butterfly自带的标签外挂把代码合并了,

+为增加的代码 -为原来的代码删除的

u7-u7.github.io效果是分页的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@PostMapping("/sendMsg")
public Result<String> sendMsg(@RequestBody User user, HttpSession session) throws MessagingException {
String phone = user.getPhone();
if (!phone.isEmpty()) {
//随机生成一个验证码
String code = MailUtils.achieveCode();
log.info(code);
//这里的phone其实就是邮箱,code是我们生成的验证码
MailUtils.sendTestMail(phone, code);
//验证码缓存到Redis,设置存活时间5分钟
redisTemplate.opsForValue().set("code", code,5, TimeUnit.MINUTES);
return Result.success("验证码发送成功");
}
return Result.error("验证码发送失败");
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@GetMapping("/list")
public Result<List<DishDto>> get(Dish dish) {
+ List<DishDto> dishDtoList;
+ String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus();
+ dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
+ //如果有,则直接返回
+ if (dishDtoList != null){
+ return Result.success(dishDtoList);
+ }
+ //如果无,则查询
//条件查询器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
//根据传进来的categoryId查询
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);
log.info("查询到的菜品信息list:{}",list);
//item就是list中的每一条数据,相当于遍历了
- List<DishDto> dishDtoList = list.stream().map((item) -> {
+ dishDtoList = list.stream().map((item) -> {
//创建一个dishDto对象
DishDto dishDto = new DishDto();
//将item的属性全都copy到dishDto里
BeanUtils.copyProperties(item, dishDto);
//由于dish表中没有categoryName属性,只存了categoryId
Long categoryId = item.getCategoryId();
//所以我们要根据categoryId查询对应的category
Category category = categoryService.getById(categoryId);
if (category != null) {
//然后取出categoryName,赋值给dishDto
dishDto.setCategoryName(category.getName());
}
//然后获取一下菜品id,根据菜品id去dishFlavor表中查询对应的口味,并赋值给dishDto
Long itemId = item.getId();
//条件构造器
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//条件就是菜品id
lambdaQueryWrapper.eq(itemId != null, DishFlavor::getDishId, itemId);
//根据菜品id,查询到菜品口味
List<DishFlavor> flavors = dishFlavorService.list(lambdaQueryWrapper);
//赋给dishDto的对应属性
dishDto.setFlavors(flavors);
//并将dishDto作为结果返回
return dishDto;
//将所有返回结果收集起来,封装成List
}).collect(Collectors.toList());
+ //将查询的结果让Redis缓存,设置存活时间为60分钟
+ redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);
return Result.success(dishDtoList);
}

因为菜品中save和update和status都需要,加入清理缓存的逻辑,单独把修改的代码放出来 同样使用diff放修改的代码

1
2
3
4
5
6
7
8
@PostMapping
public Result<String> save(@RequestBody DishDto dishDto) {
log.info("接收到的数据为:{}", dishDto);
dishService.saveWithFlavor(dishDto);
+ String key = "dish_" + dishDto.getCategoryId() + "_1";
+ redisTemplate.delete(key);
return Result.success("添加菜品成功");
}
1
2
3
4
5
6
7
8
@PutMapping
public Result<String> update(@RequestBody DishDto dishDto) {
log.info("接收到的数据为:{}", dishDto);
dishService.updateWithFlavor(dishDto);
+ String key = "dish_" + dishDto.getCategoryId() + "_1";
+ redisTemplate.delete(key);
return Result.success("修改菜品成功");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@PostMapping("/status/{status}")
public Result<String> status(@PathVariable Integer status, @RequestParam List<Long> ids) {
log.info("status:{},ids:{}", status, ids);
LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ids != null, Dish::getId, ids);
updateWrapper.set(Dish::getStatus, status);
+ LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+ lambdaQueryWrapper.in(Dish::getId, ids);
+ List<Dish> dishes = dishService.list(lambdaQueryWrapper);
+ for (Dish dish : dishes) {
+ String key = "dish_" + dish.getCategoryId() + "_1";
+ redisTemplate.delete(key);
+ }
dishService.update(updateWrapper);
return Result.success("批量操作成功");
}

删除的话不需要修改的原因的是,我们删除之前需要修改为停售状态,所以我们已经给修改状态加入了清理缓存,所以要删除的话,一定会先清理过缓存的。

SpringCache

一个框架,能实现基本注解的缓存功能,就是加注解就能清理缓存,常用注解网上都搜得到用法,我就记项目中所使用的说明一下,@Cacheable和@CacheEvict

@cacheable的作用主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

@CachEvict的作用主要针对方法配置,能够根据一定的条件对缓存进行清空

项目使用SpringCache

导入坐标

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

配置yml为Redis缓存

1
2
3
4
5
6
7
8
9
spring:
redis:
host: 172.16.1.76
port: 6379
password: 123456
database: 0
cache:
redis:
time-to-live: 1800000 # 设置缓存过期时间

缓存套餐

导入坐标配置好文件,然后在启动项加@EnableCaching 开启缓存注解

SetmealController的list方法上加上@Cacheale注解 和之前的不注解差不多 就是把值写注解里面了

1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping("/list")
@Cacheable(value = "setmealCache", key = "#setmeal.categoryId + '_' + #setmeal.status")
public Result<List<Setmeal>> list(Setmeal setmeal) {
//条件构造器
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
//添加条件
queryWrapper.eq(setmeal.getCategoryId() != null, Setmeal::getCategoryId, setmeal.getCategoryId());
queryWrapper.eq(setmeal.getStatus() != null, Setmeal::getStatus, 1);
//排序
queryWrapper.orderByDesc(Setmeal::getUpdateTime);
List<Setmeal> setmealList = setmealService.list(queryWrapper);
return Result.success(setmealList);
}

SetmealController的save、update和status方法,加入清理缓存的逻辑 很简单直接复制就行改成true全部填返回值

1
2
3
4
5
6
7
8
@PostMapping
//设置allEntries为true,清空缓存名称为setmealCache的所有缓存
@CacheEvict(value = "setmealCache", allEntries = true)
public Result<String> save(@RequestBody SetmealDto setmealDto) {
log.info("套餐信息:{}", setmealDto);
setmealService.saveWithDish(setmealDto);
return Result.success("套餐添加成功");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@PutMapping
@CacheEvict(value = "setmealCache", allEntries = true)
public R<Setmeal> updateWithDish(@RequestBody SetmealDto setmealDto){
List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
Long id = setmealDto.getId();
//根据id把对应套餐数据删除
LambdaQueryWrapper<SetmealDish> qw = new LambdaQueryWrapper<>();
qw.eq(SetmealDish::getSetmealId,id);
setmealDishService.remove(qw);
//重新添加
setmealDishes.stream().map((item) -> {
//设置属性
item.setSetmealId(id);
return item;
}).collect(Collectors.toList());
//更新套餐数据
setmealService.updateById(setmealDto);
//更新套餐对应的数据 批量保存
setmealDishService.saveBatch(setmealDishes);
return R.success(setmealDto);
}
1
2
3
4
5
6
7
8
9
@PostMapping("/status/{status}")
@CacheEvict(value = "setmealCache", allEntries = true)
public R<String> status(@PathVariable String status, @RequestParam List<Long> ids) {
LambdaUpdateWrapper<Setmeal> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(Setmeal::getId, ids);
updateWrapper.set(Setmeal::getStatus, status);
setmealService.update(updateWrapper);
return R.success("批量操作成功");
}

==!!! R需要实现Serializable接口,因为有个序列化的过程==

读写分离

读写分离比较麻烦,需要先实现MySQL的主从表,让阿贵克隆了两台虚拟机。一个为主库,一个为从库。跟着步骤来就行,唯一有问题的点就是,因为是克隆的,所以mysql需要修改uuid。这里自己出过BUG。

修改方法 查看uuid

1
2
3
4
5
6
mysql> select uuid();
+--------------------------------------+
| uuid() |
+--------------------------------------+
| 26532364-4f8d-11ed-a300-005056307198 |
+--------------------------------------+
1
vi /var/lib/mysql/auto.cnf # 修改uuid为上面查找的

别忘记重启服务

项目实现读写分离

创建一个新的分支,测试没问题再并入main

导入坐标

1
2
3
4
5
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>

修改原来的mysql配置

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
spring:
application:
name: reggie_take_out
shardingsphere:
datasource:
names:
master,slave
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.16.1.77:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.16.1.78:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
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

3. BUG点

难点(关键代码或关键配置,BUG截图+解决方案)

这是mysql主从复制,查询主从信息的时候正常的显示,但是自己显示的是Connecting to source,网上搜都说的uuid的问题,但是自己uuid修改了很多次,还是失败,就重连了一下,然后全部重新配置了uuid和Slave_Io_Running,就好了。

image-20221107161447726

4.扩展学习部分

5.总结

今天周五上午练车,下午把基本的优化知识学了,修改了原来没加入缓存和读写分离的项目,开了两台虚拟机,调配下载软件,今天的学习难度是很简单的,明天就可以部署到h3c的服务器上面了,配置方面总是会出问题的,mysql自己修了近一个半小时,linux命令也在修bug的路上,越发熟练,明天部署完停下来,梳理一下这段时间所学的知识。

11.7 部署项目在h3c

1.头:日期、所学内容出处

【黑马程序员2022新版SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术】 https://www.bilibili.com/video/BV1Fi4y1S7ix?p=31&share_source=copy_web&vd_source=c8ae4150b2286ee39a13a79bbe12b843

2.所学内容概述

配置环境

三台服务器

172.16.1.76 Redis缓存 启动Redis服务的

172.16.1.77 Nginx放前端项目和反向代理 Mysql 主从复制结构的主库

172.16.1.78 Mysql 主从复制结构的从库 jar jdk git maven 放后端项以及配置

部署前端项目

在77中修改nginx.conf 把课件中的dist文件放html目录中去

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name localhost;

location / {
root html/dist; #前端页面存放位置
index index.html;
}
location ^~ /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://172.16.1.78:8080; #后端
}
}

部署后端项目

后端这块自己出了些问题,首先安装好JDK git Mysql Maven 查看的命令也很简单

1
2
3
java -version #看jdk 和 jar
git -v # 看git
mvn -v # 看Maven

然后把IDEA中的项目执行git commit 然后push 上传到gittee码云 创建一个/usr/local/app

1
git clone https://gitee.com/u7u7-sjh/reggie_take_out.git

sh脚本 自己包名跟这个不太一样,然后自己修改了一下

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
34
35
36
#!/bin/sh
echo =================================
echo 自动化部署脚本启动
echo =================================

echo 停止原来运行中的工程
APP_NAME=reggie_take_out

tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'Stop Process...'
kill -15 $tpid
fi
sleep 2
tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'Kill Process!'
kill -9 $tpid
else
echo 'Stop Success!'
fi

echo 准备从Git仓库拉取最新代码
cd /usr/local/app/reggie_take_out #注意文件所在地址

echo 开始从Git仓库拉取最新代码
git pull
echo 代码拉取完成

echo 开始打包
output=`mvn clean package -Dmaven.test.skip=true`

cd target
echo 启动项目
nohup java -jar reggie_take_out-1.0-SNAPSHOT.jar &> reggie_take_out.log & # 注意jar包名
echo 项目启动完成

3. BUG点

难点(关键代码或关键配置,BUG截图+解决方案)

进来后端全是报错,不知道什么原因,看项目是没问题的,但是我linux中的打包是自己添加的,并不是git中克隆下来的,然后把linux中app中克隆的文件删除,重新把IDEA中main和target都上传,然后再克隆下来就没问题了。

image-20221107183351369

4.扩展学习部分

5.总结

今天上午考完科二,下午把项目部署到了h3c,就后端出了一点点问题,但是也成功了,还是小有成就的。明天上午缓冲一下,下午可以大概的进入SpringCloud的学习了,问题应该不是很大,可以带着vue学一下,感觉前端的知识还是很必要的,晚上整改一下图床的管理,买一个阿里云OSS图床,这样以后自己的Typora文件,发给别人图片加载不出来的情况也就没有了。上传到CSDN和Github 语雀也不会有图片不显示的情况了。