尚庭公寓-开发准备
6. 开发准备
6.1 准备开发环境
项目开发会用到MySQL、Redis、MinIO,本章主要内容就是部署三者,部署环境为Linux虚拟机。
6.1.1 准备Linux虚拟机
6.1.1.1 Linux快速入门
Linux入门相关内容可参考尚硅谷Linux基础。
6.1.1.2 开发环境要求
准备两台Linux虚拟机——server01
和server02
,具体要求如下
提示
开发阶段只需要一台虚拟机即可,另外一台留到后序的项目部署阶段使用。实际操作时,可先按要求准备一台,然后直接克隆得到第二台服务器。
-
操作系统为Centos7
由于下文的安装步骤都是基于Centos7系统的,所以建议统一使用Centos7系统。
-
网络设置
虚拟机网络使用NAT模式,且使用静态IP。
-
主机名设置
为两台主机分别设置一个主机名,分别是
server01
和server02
,设置主机名的命令如下hostnamectl set-hostname <主机名> --static
-
配置好SSH远程登录
ssh客户端不限,可使用Xshell、MobaXterm、finalshell等等
-
与网络时间保持同步
安装时间同步工具
chrony
。-
执行以下命令安装
chrony
yum install chrony
-
执行以下命令启动
chrony
,并设置为开机自启。systemctl start chronyd systemctl enable chronyd
知识点
-
查看时间源
chronyc sources
该命令显示结果如下
210 Number of sources = 4 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^? ntp8.flashdance.cx 2 6 3 0 +44ms[ +44ms] +/- 93ms ^? electrode.felixc.at 2 6 5 0 +32ms[ +32ms] +/- 135ms ^* 81.16.177.123 2 6 7 1 +457us[+28800s] +/- 149ms ^- dns2.synet.edu.cn 1 6 7 2 +55ms[+28800s] +/- 28ms
- MS:当前事件源的状态
- Name/IP:时间服务器地址
-
配置新的时间源
可修改
vim /etc/chrony.conf
以增加新的时间源
-
-
-
关闭防火墙
关闭命令如下
#关闭防火墙 systemctl stop firewalld#禁止防火墙开机自启 systemctl disable firewalld
-
关闭SElinux
SELinux,全称为Security-Enhanced Linux,是一种用于Linux操作系统的安全增强功能,为保证后序部署阶段的Nginx能够正常工作,此处关闭SELinux功能。
-
修改SELinux配置文件
打开配置文件
vim /etc/selinux/config
修改内容如下
SELINUX=disabled
-
重启操作系统
reboot
-
6.1.2 部署MySQL
在server01
部署MySQL,具体步骤可参考文档。
-
安装MySQL yum库
-
下载yum库
下载地址为https://dev.mysql.com/downloads/repo/yum/。需要根据操作系统选择相应版本,Centos7需选择
mysql80-community-release-el7-9.noarch.rpm
。执行以下命令可直接下载到服务器
wget https://dev.mysql.com/get/mysql80-community-release-el7-9.noarch.rpm
-
安装yum库
在上述
rpm
文件所在路径执行如下命令rpm -ivh mysql80-community-release-el7-9.noarch.rpm
-
配置国内镜像
修改
/etc/yum.repo.d/mysql-community.repo
文件中的[mysql80-community]
中的baseUrl
参数,修改内容如下:[mysql80-community] name=MySQL 8.0 Community Server baseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-8.0-community-el7-$basearch/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
-
-
安装MySQL
执行如下命令安装MySQL
yum install -y mysql-community-server
-
启动MySQL
执行如下命令启动MySQL服务
systemctl start mysqld
执行以下命令查看MySQL运行状态
systemctl status mysqld
-
root用户相关配置
-
查看root用户初始密码
MySQL启动后会将root用户的初始密码写入日志,通过以下命令可以获取密码
cat /var/log/mysqld.log | grep password
-
使用初始密码登录
执行以下命令登录MySQL
mysql -uroot -p'password'
-
修改root用户密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Atguigu.123';
注意:MySQL默认安装了validate_password 插件,默认情况下,要求密码要包含大写字母、小写字母、数字和特殊符号,且密码长度最小为8。若需设置简单密码,可禁用该插件,或调整该插件的密码强度级别。
-
授予root用户远程登录权限
CREATE USER 'root'@'%' IDENTIFIED BY 'Atguigu.123'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;
-
6.1.3 部署Redis
在server01
部署Redis服务,安装方式采用yum在线安装,安装版本为redis-7.0.13
,具体步骤如下
-
安装Redis yum仓库
-
下载yum仓库
Redis所在的仓库为remi-release,下载地址为:http://rpms.famillecollet.com/enterprise/remi-release-7.rpm,可使用如下命令直接下载到服务器
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
-
安装yum仓库
执行如下命令进行安装
rpm -ivh remi-release-7.rpm
-
-
安装Reids
执行以下命令安装Redis
yum --enablerepo=remi -y install redis-7.0.13
注:
--enablerepo
选项的作用为启用一个仓库 -
配置Redis允许远程访问
Redis服务默认只允许本地访问,若需要进行远程访问,需要做出以下配置。
修改Redis配置文件
vim /etc/redis/redis.conf
修改如下参数
#监听所有网络接口,默认只监听localhost bind 0.0.0.0#关闭保护模式,默认开启。开始保护模式后,远程访问必须进行认证后才能访问。 protected-mode no
-
启动Redis
执行以下命令启动Redis
systemctl start redis
执行以下命令查看Redis的运行状态
systemctl status redis
执行以下命令设置Redis开机自启
systemctl enable redis
6.1.4 部署MinIO
在server01
部署MinIO,安装方式采用rpm离线安装,具体步骤可参考官方文档。
-
获取MinIO安装包
下载地址如下:https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20230809233022.0.0.x86_64.rpm,通过以下命令可直接将安装包下载至服务器
wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20230809233022.0.0.x86_64.rpm
注:若下载缓慢,大家可直接使用课程资料中附带的安装包
-
安装MinIO
rpm -ivh minio-20230809233022.0.0.x86_64.rpm
-
集成Systemd
-
Systemd概述
Systemd
是一个广泛应用于Linux系统的系统初始化和服务管理器,其可以管理系统中的各种服务和进程,包括启动、停止和重启服务,除此之外,其还可以监测各服务的运行状态,并在服务异常退出时,自动拉起服务,以保证服务的稳定性。系统自带的防火墙服务firewalld
,我们自己安装的mysqld
和redis
均是由Systemd
进行管理的,此处将MinIO服务也交给Systemd管理。 -
编写MinIO服务配置文件
Systemd所管理的服务需要由一个配置文件进行描述,这些配置文件均位于
/etc/systemd/system/
或者/usr/lib/systemd/system/
目录下,下面创建MinIO服务的配置文件。执行以下命令创建并打开
minio.service
文件vim /etc/systemd/system/minio.service
内容如下,具体可参考MinIO官方文档。
[Unit] Description=MinIO Documentation=https://min.io/docs/minio/linux/index.html Wants=network-online.target After=network-online.target AssertFileIsExecutable=/usr/local/bin/minio[Service] WorkingDirectory=/usr/local ProtectProc=invisible EnvironmentFile=-/etc/default/minio ExecStartPre=/bin/bash -c "if [ -z \"${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/default/minio\"; exit 1; fi" ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES Restart=always LimitNOFILE=65536 TasksMax=infinity TimeoutStopSec=infinity SendSIGKILL=no[Install] WantedBy=multi-user.target
注意:
重点关注上述文件中的以下内容即可
EnvironmentFile
,该文件中可配置MinIO服务所需的各项参数ExecStart
,该参数用于配置MinIO服务的启动命令,其中$MINIO_OPTS
、$MINIO_VOLUMES
,均引用于EnvironmentFile
中的变量。MINIO_OPTS
用于配置MinIO服务的启动选项,可省略不配置。MINIO_VOLUMES
用于配置MinIO服务的数据存储路径。
Restart
,表示自动重启
-
编写
EnvironmentFile
文件执行以下命令创建并打开
/etc/default/minio
文件vim /etc/default/minio
内容如下,具体可参考官方文档。
MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=minioadmin MINIO_VOLUMES=/data MINIO_OPTS="--console-address :9001"
注意
-
MINIO_ROOT_USER
和MINIO_ROOT_PASSWORD
为用于访问MinIO的用户名和密码,密码长度至少8位。 -
MINIO_VOLUMES
用于指定数据存储路径,需确保指定的路径是存在的,可执行以下命令创建该路径。mkdir /data
-
MINIO_OPTS
中的console-address
,用于指定管理页面的地址。
-
-
-
启动MinIO
执行以下命令启动MinIO
systemctl start minio
执行以下命令查询运行状态
systemctl status minio
设置MinIO开机自启
systemctl enable minio
-
访问MinIO管理页面
管理页面的访问地址为:
http://192.168.10.101:9001
注意:
ip
需要根据实际情况做出修改
6.2 技术储备
6.2.1 MyBatis Plus快速入门
6.2.2.1 概述
MyBatis-Plus(简称 MP)是一个MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。其突出的特性如下:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,提供了大量的通用的CRUD方法,因此可以省去大量手写sql的语句的工作。
- 条件构造器:提供了强大的条件构造器,可以构造各种复杂的查询条件,以应对各种复杂查询。
- 内置分页插件:配置好插件之后,写分页等同于普通 List 查询,无需关注分页逻辑。
下面通过一个简单案例快速熟悉MyBatis Plus的基本使用
6.2.2.2 数据库准备
首先在数据库中准备一张表,为后序的学习做准备。
-
创建数据库
在MySQL中创建一个数据库
hello_mp
CREATE DATABASE hello_mp CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-
创建表
在
hello-mp
库中创建一个表user
DROP TABLE IF EXISTS user; CREATE TABLE user (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id) );
-
插入数据
INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
6.2.2.3 与SpringBoot集成
Mybatis Plus与SpringBoot的集成十分简单,具体操作如下
-
引入Maven 依赖
提前创建好一个SpringBoot项目,然后在项目中引入MyBatis Plus依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version> </dependency>
本案例完整的
pom.xml
文件如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.9</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atguigu</groupId><artifactId>hello-mp</artifactId><version>0.0.1-SNAPSHOT</version><name>hello-mp</name><description>hello-mp</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
-
配置
application.yml
文件配置数据库相关内容如下
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: Atguigu.123url: jdbc:mysql://192.168.10.101:3306/hello_mp?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8
6.2.2.4 创建实体类
创建与user
表相对应的实体类,如下
@Data
@TableName("user")
public class User {@TableId(value = "id", type = IdType.AUTO)private Long id;@TableField("name")private String name;@TableField("age")private Integer age;@TableField("email")private String email;
}
知识点:
实体类中的三个注解的含义如下
-
@TableName
:表名注解,用于标识实体类所对应的表value
:用于声明表名
-
@TableId
:主键注解,用于标识主键字段value
:用于声明主键的字段名type
:用于声明主键的生成策略,常用的策略有AUTO
、ASSIGN_UUID
、INPUT
等等
-
@TableField
:普通字段注解,用于标识属性所对应的表字段value
:用于声明普通字段的字段名
6.2.2.4 通用Mapper
通用Mapper提供了通用的CRUD方法,使用它可以省去大量编写简单重复的SQL语句的工作,具体用法如下
-
创建Mapper接口
创建
UserMapper
接口,并继承由Mybatis Plus提供的BaseMapper<T>
接口,如下@Mapper public interface UserMapper extends BaseMapper<User> { }
知识点:
若Mapper接口过多,可不用逐一配置
@Mapper
注解,而使用@MapperScan
注解指定包扫描路径进行统一管理,例如@SpringBootApplication @MapperScan("com.atguigu.hellomp.mapper") public class HelloMpApplication {public static void main(String[] args) {SpringApplication.run(HelloMpApplication.class, args);} }
-
测试通用Mapper
创建
userMapperTest
测试类型,内容如下@SpringBootTest class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelectList() {List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}@Testpublic void testSelectById() {User user = userMapper.selectById(1);System.out.println(user);}@Testpublic void testInsert() {User user = new User();user.setName("zhangsan");user.setAge(11);user.setEmail("test@test.com");userMapper.insert(user);}@Testpublic void testUpdateById() {User user = userMapper.selectById(1);user.setName("xiaoming");userMapper.updateById(user);}@Testpublic void testDeleteById() {userMapper.deleteById(1);} }
6.2.2.5 通用Service
通用Service进一步封装了通用Mapper的CRUD方法,并提供了例如saveOrUpdate
、saveBatch
等高级方法。
-
创建Service接口
创建
UserService
,内容如下public interface UserService extends IService<User> { }
-
创建Service实现类
创建
UserServiceImpl
,内容如下@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
-
测试通用Service
创建
UserServiceImplTest
测试类,内容如下@SpringBootTest class UserServiceImplTest {@Autowiredprivate UserService userService;@Testpublic void testSaveOrUpdate() {User user1 = userService.getById(2);user1.setName("xiaohu");User user2 = new User();user2.setName("lisi");user2.setAge(27);user2.setEmail("lisi@email.com");userService.saveOrUpdate(user1);userService.saveOrUpdate(user2);}@Testpublic void testSaveBatch() {User user1 = new User();user1.setName("dongdong");user1.setAge(49);user1.setEmail("dongdong@email.com");User user2 = new User();user2.setName("nannan");user2.setAge(29);user2.setEmail("nannan@email.com");List<User> users = List.of(user1, user2);userService.saveBatch(users);} }
6.2.2.6 条件构造器
条件构造器用于构造复杂的查询条件,例如获取name='zhangsan'
的用户。MyBatis Plus共提供了两类构造器,分别是QueryWrapper
和UpdateWrapper
。其中QueryWrapper
主要用于查询、删除操作,UpdateWrapper
主要用于更新操作,下面通过几个案例学习条件构造器的基础用法。
-
创建
WrapperTest
测试类,内容如下@SpringBootTest public class WrapperTest {@Autowiredprivate UserService userService;@Testpublic void testQueryWrapper() {//查询name=Tom的所有用户QueryWrapper<User> queryWrapper1 = new QueryWrapper<>();queryWrapper1.eq("name", "Tom");//查询邮箱域名为baomidou.com的所有用户QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();queryWrapper2.like("email", "baomidou.com");//查询所有用户信息并按照age字段降序排序QueryWrapper<User> queryWrapper3 = new QueryWrapper<>();queryWrapper3.orderByDesc("age");//查询age介于[20,30]的所有用户QueryWrapper<User> queryWrapper4 = new QueryWrapper<>();queryWrapper4.between("age", 20, 30);//查询age小于20或大于30的用户QueryWrapper<User> queryWrapper5 = new QueryWrapper<>();queryWrapper5.lt("age", 20).or().gt("age", 30);//邮箱域名为baomidou.com且年龄小于30或大于40且的用户QueryWrapper<User> queryWrapper6 = new QueryWrapper<>();queryWrapper6.like("email", "baomidou.com").and(wrapper -> wrapper.lt("age", 30).or().gt("age", 40));List<User> list = userService.list(queryWrapper6);list.forEach(System.out::println);}@Testpublic void testUpdateWrapper() {//将name=Tom的用户的email改为Tom@baobidou.comUpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("name", "Tom");updateWrapper.set("email", "Tom@baobidou.com");userService.update(updateWrapper);} }
-
创建
LambdaWrapperTest
测试类,内容如下上述的
QueryWrapper
和UpdateWrapper
均有一个Lambda
版本,也就是LambdaQueryWrapper
和LambdaUpdateWrapper
,Lambda
版本的优势在于,可以省去字段名的硬编码,具体案例如下:@SpringBootTest public class LambdaWrapperTest {@Autowiredprivate UserService userService;@Testpublic void testLambdaQueryWrapper() {//查询name=Tom的所有用户LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(User::getName, "Tom");List<User> list = userService.list(lambdaQueryWrapper);list.forEach(System.out::println);}@Testpublic void testLambdaUpdateWrapper() {//将name=Tom的用户的邮箱改为Tom@tom.comLambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(User::getName, "Tom");lambdaUpdateWrapper.set(User::getEmail, "Tom@Tom.com");userService.update(lambdaUpdateWrapper);} }
6.2.2.7 分页插件
分页查询是一个很常见的需求,故Mybatis-Plus提供了一个分页插件,使用它可以十分方便的完成分页查询。下面介绍Mybatis-Plus分页插件的用法,详细信息可参考官方文档。
-
配置分页插件
创建
com.atguigu.hellomp.config.MPConfiguration
配置类,增加如下内容@Configuration public class MPConfiguration {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;} }
-
分页插件使用说明
-
构造分页对象
分页对象包含了分页的各项信息,其核心属性如下:
属性名 类型 默认值 描述 records List emptyList 查询数据列表 total Long 0 查询列表总记录数 size Long 10 每页显示条数,默认 10
current Long 1 当前页 分页对象既作为分页查询的参数,也作为分页查询的返回结果,当作为查询参数时,通常只需提供
current
和size
属性,如下IPage<T> page = new Page<>(current, size);
注:
IPage
为分页接口,Page
为IPage
接口的一个实现类。 -
分页查询
Mybatis Plus的
BaseMapper
和ServiceImpl
均提供了常用的分页查询的方法,例如:-
BaseMapper
的分页查询:IPage<T> selectPage(IPage<T> page,Wrapper<T> queryWrapper);
-
ServiceImpl
的分页查询:// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
-
自定义Mapper
对于自定义SQL,也可以十分方便的完成分页查询,如下
Mapper
接口:IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
Mapper.xml
:<select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">SELECT id,name FROM user WHERE state=#{state} </select>
注意:
Mapper.xml
中的SQL只需实现查询list
的逻辑即可,无需关注分页的逻辑。
-
-
-
案例实操
分页查询案例如下:
创建
PageTest
测试类,内容如下@SpringBootTest public class PageTest {@Autowiredprivate UserService userService;@Autowiredprivate UserMapper userMapper;//通用Service分页查询@Testpublic void testPageService() {Page<User> page = new Page<>(2, 3);Page<User> userPage = userService.page(page);userPage.getRecords().forEach(System.out::println);}//通用Mapper分页查询@Testpublic void testPageMapper() {IPage<User> page = new Page<>(2, 3);IPage<User> userPage = userMapper.selectPage(page, null);userPage.getRecords().forEach(System.out::println);}//自定义SQL分页查询@Testpublic void testCustomMapper() {IPage<User> page = new Page<>(2, 3);IPage<User> userPage = userMapper.selectUserPage(page);userPage.getRecords().forEach(System.out::println);} }
在UserMapper中声明分页查询方法如下
IPage<User> selectUserPage(IPage<User> page);
创建
resources/mapper/UserMapper.xml
文件,内容如下<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atguigu.hellomp.mapper.UserMapper"><select id="selectUserPage" resultType="com.atguigu.hellomp.entity.User">select *from user</select> </mapper>
注意:
Mybatis-Plus中
Mapper.xml
文件路径默认为:classpath*:/mapper/**/*.xml
,可在application.yml
中配置以下参数进行修改mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml
6.2.2.8 MyBatisX插件
MyBatis Plus提供了一个IDEA插件——MybatisX
,使用它可根据数据库快速生成Entity
、Mapper
、Mapper.xml
、Service
、ServiceImpl
等代码,使用户更专注于业务。
下面演示具体用法
-
安装插件
在IDEA插件市场搜索
MyBatisX
,进行在线安装 -
配置数据库连接
在IDEA中配置数据库连接
-
生成代码
首先将之前编写的
User
、UserMapper
、UserServcie
、UserServiceImpl
全部删除,然后按照下图指示使用插件生成代码配置实体类相关信息
配置代码模版信息
点击Finish然后查看生成的代码。
6.2.2 MinIO快速入门
6.2.2.1 MinIO核心概念
下面介绍MinIO中的几个核心概念,这些概念在所有的对象存储服务中也都是通用的。
-
对象(Object)
对象是实际的数据单元,例如我们上传的一个图片。
-
存储桶(Bucket)
存储桶是用于组织对象的命名空间,类似于文件夹。每个存储桶可以包含多个对象。
-
端点(Endpoint)
端点是MinIO服务器的网络地址,用于访问存储桶和对象,例如
http://192.168.10.101:9000
注意:
9000
为MinIO的API的默认端口,前边配置的9001
以为管理页面端口。 -
Access Key 和 Secret Key
Access Key是用于标识和验证访问者身份的唯一标识符,相当于用户名。
Secret Key是与Access Key关联的密码,用于验证访问者的身份。
6.2.2.2 MinIO管理页面操作
-
登录
管理页面的地址为http://192.168.10.101:9001,登录的用户名和密码为部署时在
EnvironmentFile
文件中配置的如下参数MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=minioadmin
-
创建存储桶
-
上传图片
-
找到目标桶
-
上传图片
-
-
访问图片
-
图片URL
由于MinIO提供了HTTP访问功能,所以可以通过浏览器直接访问对象。对象URL为MinIO的
Endpoint
+对象的存储路径
,例如下图中的图片对象的URL为http:192.168.10.101:9000/test/公寓-外观.jpg。 -
访问权限
不出意外的话,使用浏览器访问上述URL,会得到如下响应,很显然是没有访问权限。
<Error><Code>AccessDenied</Code><Message>Access Denied.</Message><Key>公寓-外观.jpg</Key><BucketName>test</BucketName><Resource>/test/公寓-外观.jpg</Resource><RequestId>177BC92022FC5684</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId> </Error>
若想继续访问图片,需要修改图片所在桶的访问权限,如下图所示
如上图所示,可选的访问权限共有三个选项,分别是
Private
、Public
和Custom
,具体说明如下-
Private
只允许桶的所有者对该桶进行读写。
-
Public
允许所有人对该桶进行读写。
-
Custom
自定义访问权限。
若想将权限设置为只允许所有者写,但允许所有人读,就需要自定义访问权限。自定义访问权限,需要使用一个规定格式的JSON字符串进行描述,具体格式可参考官方文档。
例如以下JSON字符串表达的含义是:允许(
Allow
)所有人(*
)读取(s3:GetObject
)指定桶(test
)的所有内容。{"Statement" : [ {"Action" : "s3:GetObject","Effect" : "Allow","Principal" : "*","Resource" : "arn:aws:s3:::test/*"} ],"Version" : "2012-10-17" }
将
test
桶访问权限设置为Custom
,并添加上述内容重新访问http:192.168.10.101:9000/test/公寓-外观.jpg,观察是否正常。
-
-
6.2.2.3 MinIO Java SDK
MinIO提供了多种语言的SDK供开发者使用,本项目需要用到Java SDK,下面通过一个简单案例熟悉一下其基本用法,具体内容可参考官方文档。
-
创建一个Maven项目
-
引入如下依赖
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.3</version> </dependency>
-
编写如下内容
public class App {public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException {try {//构造MinIO ClientMinioClient minioClient = MinioClient.builder().endpoint("http://192.168.10.101:9000").credentials("minioadmin", "minioadmin").build();//创建hello-minio桶boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("hello-minio").build());if (!found) {//创建hello-minio桶minioClient.makeBucket(MakeBucketArgs.builder().bucket("hello-minio").build());//设置hello-minio桶的访问权限String policy = """{"Statement" : [ {"Action" : "s3:GetObject","Effect" : "Allow","Principal" : "*","Resource" : "arn:aws:s3:::hello-minio/*"} ],"Version" : "2012-10-17"}""";minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket("hello-minio").config(policy).build());} else {System.out.println("Bucket 'hello-minio' already exists.");}//上传图片minioClient.uploadObject(UploadObjectArgs.builder().bucket("hello-minio").object("公寓-外观.jpg").filename("D:\\workspace\\hello-minio\\src\\main\\resources\\公寓-外观.jpg").build());System.out.println("上传成功");} catch (MinioException e) {System.out.println("Error occurred: " + e);}} }
-
运行测试
运行上述代码,然后查看MinIO管理页面,观察是否上传成功。
6.2.3 Redis快速入门
6.2.3.1 Redis概述
-
Reids定义
Redis(Remote Dictionary Server)是一个基于内存的键值对存储系统,常用作缓存服务。由于Reids将数据都保存在内存中,因此其读写性能十分惊人,同时,为保证数据的可靠性,Redis会将数据备份到硬盘上,用于故障发生时的数据恢复。
-
Redis特点
- 高性能:Redis主要将数据存储在内存中,因此读写速度非常快,适合对速度有较高要求的场景。
- 支持多种数据结构:Redis中键值对的值(Value)支持多种数据结构,如字符串、哈希表、列表、集合等,这使得它可以应用于多种不同的场景。
- 持久化:Redis可以通过定期快照或者实时记录写操作日志的方式将内存中的数据持久化到硬盘,确保数据在重启后不会丢失。
- 灵活的数据过期策略:可以为每个键设置过期时间,一旦过期,Redis会自动删除。
-
Redis应用场景
Redis最为常见的一个应用场景就是用作缓存,缓存可以显著提升访问速度,降低数据库压力。
6.2.3.2 Reids客户端
-
命令行客户端
Reids提供了一个命令行客户端
redis-cli
,我们可以使用它连接到Redis服务器,然后通过命令与服务器进行各种交互,例如数据的增删改查。下面介绍该命令的基本用法。-
启动客户端
redis-cli -h 127.0.0.1 -p 6379
说明:
-h <hostname>
选项用于声明Redis服务器的主机名或IP地址,默认值为127.0.0.1
。-p <port>
选项用于声明Redis服务器监听的端口号,默认值为6379
。
-
测试连接状态
ping
命令可用于测试连接状态,语法如下ping
说明:若连接正常,则会返回pong。
-
退出客户端
quit
命令可用于断开客户端与服务端的连接,并退出客户端,语法如下quit
-
-
图形化客户端
Redis官方推荐的图形化客户端为RedisInsight,该客户端开源免费,且功能强大,下面演示如何使用。
-
安装
安装包为
RedisInsight-v2-win-installer.exe
,可在项目资料中获取,安装步骤比较简单,大家自行安装即可。 -
选择配置数据库连接
-
填写连接信息,并测试连接,通过后保存连接
-
点击创建好的连接
-
工作界面如下
-
6.2.3.3 Redis常用数据类型及命令
6.2.3.3.1 通用命令
-
查看所有键
keys
命令可用于查看所有键,语法如下keys pattern
说明:pattern用于匹配key,其中
*
表示任意个任意字符,?
表示一个任意字符。示例:
127.0.0.1:6379> KEYS * 1) "k3" 2) "k2" 3) "k1"
注意:该命令会遍历Redis服务器中保存的所有键,因此当键很多时会影响整个Redis服务的性能,线上环境需要谨慎使用。
-
键总数
dbsize
可用于查看键的总数,语法如下dbsize
-
判断键是否存在
exists
命令可用于判断一个键是否存在,语法如下exists key
说明:若键存在则返回1,不存在则返回0。
-
删除键
del
可用于删除指定键,语法如下del key [key ...]
说明:返回值为删除键的个数,若删除一个不存在的键,则返回0。
-
查询键的剩余过期时间
ttl key
说明:
ttl
的含义为time to live,用于查询一个定时键的剩余存活时间,返回值以秒为单位。若查询的键的未设置过期时间,则返回-1
,若查询的键不存在,则返回-2
。 -
数据库管理命令
Redis默认有编号为0~15的16个逻辑数据库,每个数据库之间的数据是相互独立的,所有连接默认使用的都是0号数据库。
-
切换数据库
select
命令可用于切换数据库,语法如下select index
说明:若index超出范围,会报错
-
清空数据库
flushdb
命令会清空当前所选用的数据库,flushall
命令会清空0~15号所有的数据库。注意:生产环境慎用
-
6.2.3.3.2 string类型
-
概述
Redis中的string类型保存的是字节序列(Sequence of bytes),因此任意类型的数据,只要经过序列化之后都可以保存到Redis的string类型中,包括文本、数字甚至是一个对象。
-
常用命令
-
set
set
命令用于添加string类型的键值对,具体语法如下SET key value [NX|XX] [EX seconds|PX milliseconds]
各选项含义如下
- NX:仅在key不存在时set
- XX:仅在key存在时set
- EX seconds:设置过期时间,单位为秒
- PX milliseconds:设置过期时间,单位为毫秒
-
get
get
命令用于获取某个string类型的键对应的值,具体语法如下GET key
-
incr
incr
命令用于对数值做自增操作,具体语法如下INCR key
若key对应的value是整数,则返回自增后的结果,若不是整数则报错,若key不存在则创建并返回1。
-
decr
decr
命令用于对数值做自减操作,具体语法如下DECR key
若key对应的value是整数,则返回自减后的结果,若不是整数则报错,若key不存在则创建并返回-1。
-
-
应用场景
string类型常用于缓存、计数器等场景。
6.2.3.3.3 list类型
-
概述
list类型可用于存储多个string类型的元素,并且所有元素按照被添加的顺序存储。
-
常用命令
list类型相关的命令较多,下面分类进行进行介绍。
-
添加元素
向列表中添加元素的命令有
lpush
、rpush
、linsert
,各命令的功能与用法如下-
lpush
该命令用于向list左侧添加元素,语法如下
lpush key element [element ...]
示例
lpush l1 a b c
-
rpush
该命令用于向list右侧添加元素,语法如下
rpush key element [element ...]
-
linsert
该命令用于向list指定位置添加元素,语法如下
linsert key before|after pivot element
示例
linsert l1 after b new
-
-
查询元素
查询list元素的命令有
lindex
和lrange
,各命令的功能与用法如下-
lindex
该命令用于获取指定索引位置的元素,语法如下
lindex key index
说明:index从左到右依次是0,1,2…,从右到左依次是-1,-2,-3…
-
lrange
该命令用于获取指定范围内的元素列表,语法如下
lrange key start stop
示例
获取list全部元素,命令如下
lrange l1 0 -1
-
-
删除元素
删除list元素的命令有
lpop
、rpop
、lrem
,各命令的功能与用法如下-
lpop
该命令用于移除并返回list左侧元素,语法如下
lpop key [count]
说明:count参数表示移除元素的个数
-
rpop
该命令用于移除并返回list右侧的元素,语法如下
rpop key [count]
-
lrem
该命令用于移除list中的指定元素,语法如下
lrem key count element
说明:count参数表示要移除element元素的个数(list中可以存在多个相同的元素),count的用法如下
- 若count>0,则从左到右删除最多count个element元素
- 若count<0,则从右到左删除最多count(的绝对值)个element元素
- 若count=0,则删除所有的element元素
-
-
修改元素
lset
命令可用于修改指定索引位置的元素,语法如下lset key index element
-
其他
llen
命令可用于查看list长度,语法如下llen key
-
-
应用场景
- 社交应用中,可使用list缓存每个用户发布的最新的N条记录。
- list可用作异步消息队列。
6.2.3.3.4 set类型
-
概述
和list类型相似,set类型也可用来存储多个string类型的元素,但与list类型不同,set中的元素是无序的,且set中不会包含相同元素。
-
常用命令
-
集合内
-
sadd
该命令用于向set中添加元素,语法如下
sadd key member [member ...]
-
smembers
该命令用于查询set中的全部元素,语法如下
smembers key
-
srem
该命令用于移除set中的指定元素,语法如下
srem key member [member ...]
-
spop
该命令随机移除并返回set中的n个元素,语法如下
spop key [count]
-
srandmember
该命令随机返回set中的n个元素(不删除),语法如下
srandmember key [count]
-
scard(Cardinality,基数)
该命令用于查询set中的元素个数,语法如下
scard key
-
sismember
该命令用于元素是否在set中,语法如下
sismember key element
-
-
集合间
-
sinter
该命令用于计算多个集合的交集,语法如下
sinter key [key ...]
-
sunion
该命令用于计算多个集合的并集,语法如下
sunion key [key ...]
-
sdiff
该命令用于计算多个集合的差集,语法如下
sdiff key [key ...]
-
-
-
应用场景
set可用于计算共同关注好友,随机抽奖系统等等。
6.2.3.3.5 hash类型
-
概述
hash类型类似于Java语言中的
HashMap
,可用于存储键值对。 -
常用命令
-
hset
该命令用于向hash中增加键值对,语法如下
hset key field value [field value ...]
-
hget
该命令用于获取hash中某个键对应的值,语法如下
hget key field
-
hdel
该命令用于删除hash中的指定的键值对,语法如下
hdel key field [field ...]
-
hlen
该命令用于查询hash中的键值对个数,语法如下
hlen key
-
hexists
该命令用于判断hash中的某个键是否存在,语法如下
hexists key field
-
hkeys
该命令用于返回hash中所有的键,语法如下
hkeys key
-
hvals
该命令用于返回hash中所有的值,语法如下
hvals key
-
hgetall
该命令用于返回hash中所有的键与值,语法如下
hgetall key
-
-
应用场景
hash类型可用于缓存对象等。
6.2.3.3.6 zset类型
-
概述
zset(sorted set)被称为有序集合,同set相似,zset中也不会包含相同元素,但不同的是,zset中的元素是有序的。并且zset中的元素并非像list一样按照元素的插入顺序排序,而是按照每个元素的分数(score)排序。
-
常用命令
-
zadd
该命令用于向zset中添加元素,语法如下
ZADD key [NX|XX] score member
说明:
- NX:仅当member不存在时才add
- XX:仅当member存在时才add
-
zcard
该命令用于计算zset中的元素个数,语法如下
zcard key
-
zscore
改名用于查看某个元素的分数,语法如下
zscore key member
-
zrank/zrevrank
这组命令用于计算元素的排名,其中zrank按照score的升序排序,zrevrank则按照降序排序,语法如下
zrank/zrevrank key member
**说明:**名次从0开始。
-
zrem
该命令用于删除元素,语法如下
zrem key member [member ...]
-
zincrby
该命令用于增加元素的分数,语法如下
zincrby key increment member
-
zrange
该命令用于查询指定区间范围的元素,语法如下
zrange key start stop [byscore] [rev] [limit offset count] [withscores]
说明:
- start/stop:用于指定查询区间,但是在不同模式下,其代表的含义也不相同
- 默认模式下,
start~stop
表示的是名次区间,且该区间为闭区间。名次从0开始,且可为负数,-1表示倒数第一,-2表示倒数第二,以此类推。 - byscore模式下(声明了byscore参数),则
start~stop
表示的就是分数区间,该区间默认仍为闭区间。在该模式下,可以在start
或stop
前增加(
来表示开区间,例如(1 (5
,表示的就是(1,5)
这个开区间。除此之外,还可以使用-inf
和+inf
表示负无穷和正无穷。
- 默认模式下,
- byscore:用于切换到分数模式
- rev:表示降序排序。在byscore模式下使用rev参数需要注意查询区间,start应大于stop。
- limit:该选项只用于byscore模式,作用和sql语句中的limit一致
- withscores:用于打印分数
- start/stop:用于指定查询区间,但是在不同模式下,其代表的含义也不相同
-
-
应用场景
zset主要用于各种排行榜。
6.2.3.4 SpringBoot整合Redis
6.2.3.4.1 Spring Data Redis概述
Spring Data Redis 是Spring大家族中的一个子项目,主要用于Spring程序和Redis的交互。它基于的Redis Java客户端(Jedis和Lettuce)做了抽象,提供了一个统一的编程模型,使得Spring程序与Redis的交互变得十分简单。
Spring Data Redis 中有一个十分重要的类——RedisTemplate
,它封装了与Redis进行的交互的各种方法,我们主要用使用它与Redis进行交互。
6.2.3.4.2 Spring Data Redis快速入门
-
创建SpringBoot项目
-
引入Maven依赖
Spring Boot提供了对Spring Data Redis的支持,在Spring Boot项目中可以直接引入
spring-boot-starter-data-redis
来完成Spring Data Redis的自动配置,具体依赖如下<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置application.yml文件
在
application.yml
文件中增加如下参数spring:data:redis:host: 192.168.10.101port: 6379database: 0
-
RedisTemplate使用
由于
spring-boot-starter-data-redis
中提供了RedisTemplate
的自动配置,所以我们可以将RedisTemplate
注入自己的类中,如下边的案例所示@SpringBootTest public class TestRedisTemplate {@Autowiredprivate RedisTemplate redisTemplate;@Testpublic void testRedisTemplate() {} }
根据Redis的数据类型,RedisTemplate对各种交互方法做了分组,以下是常用的几个分组
分组 说明 redisTemplate.opsForValue()
操作string类型的方法 redisTemplate.opsForList()
操作list类型的方法 redisTemplate.opsForSet()
操作set类型的方法 redisTemplate.opsForHash()
操作hash类型的方法 redisTemplate.opsForZSet()
操作zset类型的方法 redisTemplate
通用方法 下面简单测试几个简单的方法
@SpringBootTest public class TestRedisTemplate {@Autowiredprivate RedisTemplate redisTemplate;@Testpublic void testSet() {redisTemplate.opsForValue().set("key1", "value1");}@Testpublic void testGet() {String result = (String) redisTemplate.opsForValue().get("key1");System.out.println(result);}@Testpublic void testDel() {redisTemplate.delete("key1");} }
-
序列化问题
-
问题演示
-
问题一
使用RedisTemplate向Redis中增加一个键值对
redisTemplate.opsForValue().set("key2","value2");
使用RedisTemplate查询key2所对应的value,有结果
redisTemplate.opsForValue().get("key2");
使用命令行客户端查询key2所对应的value,无结果
get key2
-
问题二
在图形化客户端或者命令行客户端观察key2,显示异常
RedisInsight中的key2显示如下
命令行客户端中的key2显示如下
-
-
问题说明
上述问题的根本原因是,Redis中的key和value均是以二进制的形式存储的,因此客户端输入的key和value都会经过序列化之后才发往Redis服务端。而RedisTemplate所使用序列化方式和命令行客户端采用序列化方式不相同,进而导致序列化之后的二进制数据不同,所以才会导致上述的现象。
-
-
StringRedisTemplate使用
为解决上述问题,可使用
StringRedisTemplate
代替RedisTemplate
,因为StringRedisTemplate
使用的序列化器和命令行所使用的序列化器是相同的。spring-boot-starter-data-redis
同样提供了StringRedisTemplate
的自动配置,因此我们也可以直接将其注入到自己的类中。实例代码如下@SpringBootTest public class TestStringRedisTemplate {@Autowiredprivate StringRedisTemplate redisTemplate;@Testpublic void testSet() {redisTemplate.opsForValue().set("key4", "value4");}@Testpublic void testGet() {String result = redisTemplate.opsForValue().get("key4");System.out.println(result);}@Testpublic void testDel() {redisTemplate.delete("key4");} }
6.2.4 Knife4j快速入门
6.2.4.1 概述
Knife4j是一个用于生成和展示API文档的工具,同时它还提供了在线调试的功能,下图是其工作界面。
了解:
- Knife4j有多个版本,最新版的Knife4j基于开源项目
springdoc-openapi
,这个开源项目的核心功能就是根据SpringBoot项目中的代码自动生成符合OpenAPI规范的接口信息。 - OpenAPI规范定义接口文档的内容和格式,其前身是
Swagger
规范。
6.2.4.2 与SpringBoot集成
与SpringBoot的集成相对简单,具体操作如下
-
创建SpringBoot项目
-
引入Maven 依赖
Knife4j的依赖如下
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.3.0</version> </dependency>
项目完整的pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.9</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atguigu</groupId><artifactId>hello-knife4j</artifactId><version>0.0.1-SNAPSHOT</version><name>hello-knife4j</name><description>hello-knife4j</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.3.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build> </project>
-
创建配置类
创建
com.atguigu.helloknife4j.config.Knife4jConfiguration
,内容如下@Configuration public class Knife4jConfiguration {@Beanpublic OpenAPI openAPI() {return new OpenAPI().info(new Info().title("hello-knife4j项目API").version("1.0").description("hello-knife4j项目的接口文档"));}@Beanpublic GroupedOpenApi userAPI() {return GroupedOpenApi.builder().group("用户信息管理").pathsToMatch("/user/**").build();}@Beanpublic GroupedOpenApi systemAPI() {return GroupedOpenApi.builder().group("产品信息管理").pathsToMatch("/product/**").build();} }
-
启动项目
启动SpringBoot项目,访问http://localhost:8080/doc.html,观察接口文档。
6.2.4.3 基本使用
Knife4j的使用也十分简单,我们只需使用几个简单注解,对接口进行描述,Knife4j就能自动生成API文档了。具体操作如下
-
描述实体类
创建
com.atguigu.helloknife4j.entity.User
,内容如下@Data @Schema(description = "用户信息实体") public class User {@Schema(description = "编号")private Long id;@Schema(description = "用户姓名")private String name;@Schema(description = "用户年龄")private Integer age;@Schema(description = "用户邮箱")private String email; }
知识点:
@Schema
注解用于描述作为接口参数或者返回值的实体类的数据结构。 -
描述Controller接口
创建
com.atguigu.helloknife4j.controller.HelloController
,内容如下@RestController @RequestMapping("/user") @Tag(name = "用户信息管理") public class HelloController {@Operation(summary = "根据id获取用户信息")@GetMapping("getById")public User getUserById(@Parameter(description = "用户id") @RequestParam Long id) {User user = new User();user.setId(id);user.setName("zhangsan");user.setAge(11);user.setEmail("zhangsan@email.com");return user;} }
知识点:
@Tag
注解用于对接口进行分类,相同Tag
的接口会放在同一个菜单。@Operation
用于对接口进行描述。@Parameter
用于对HTTP请求参数进行描述