# xiaohanbao **Repository Path**: mml520/xiaohanbao ## Basic Information - **Project Name**: xiaohanbao - **Description**: 小憨包外卖,是一个使用Vue+SpringBoot 前后端分离系统 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-07-17 - **Last Updated**: 2022-07-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 外卖项目学习文档 ### 静态资源映射 ①新建配置类 WebMvcConfig ②继承 WebMvcConfigurationSupport ③实现addResourceHandlers方法 ④映射静态资源路径 ​ addResourceHandler("/backend/**") 请求的路径 ​ addResourceLocations("classpath:/backend/") 映射的路径 ```java @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/backend/**") .addResourceLocations("classpath:/backend/"); registry.addResourceHandler("/front/**") .addResourceLocations("classpath:/front/"); } } ``` ### MD5加密 **DigestUtils** 工具类 调用 **md5DigestAsHex** 方法 参数为需要加密的字符串 ```java DigestUtils.md5DigestAsHex(password.getBytes()); ``` ### 拦截器 ①给类加上 **@WebFilter** 注解 ②属性 **filterName** 过滤器的名称 ③属性 **urlPatterns** 拦截的路径 ④实现 过滤器接口 **Filter** ⑤实现 **doFilter** 方法 ### Spring Boot @WebFilter不起作用的问题 Spring MVC直接注解 **@WebFilter** 就可以了,但是Spring Boot上这样配置并没有起作用。需要加上 **@Configuration** ### 路径匹配器 **AntPathMatcher** 支持通配符匹配 使用方法 ①**new AntPathMatcher** 对象 ```java public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); ``` ②调用 **match** 方法进行路径匹配 参数1 定义好的路径 参数2 传过来的路径 ```java boolean match = PATH_MATCHER.match(url, requestUrl); ``` ### 异常类 ①给类加注解 **@ControllerAdvice** ```java @ControllerAdvice(annotations = {RestController.class, Controller.class}) ``` ②给捕获异常的方法加 注解 **@ExceptionHandler** 参数为需要捕获的异常类型 ```java @ExceptionHandler(SQLIntegrityConstraintViolationException.class) ``` ### 分页插件 ①新建一个配置类 添加注解 **@Configuration** ②固定写法 ```java @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } ``` ### 扩展mvc框架的消息转换器 ①新建一个对象映射器 ```java public class JacksonObjectMapper extends ObjectMapper { public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; public JacksonObjectMapper() { super(); //收到未知属性时不报异常 this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); //反序列化时,属性不存在的兼容处理 this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); SimpleModule simpleModule = new SimpleModule() .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))) .addSerializer(BigInteger.class, ToStringSerializer.instance) .addSerializer(Long.class, ToStringSerializer.instance) .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); //注册功能模块 例如,可以添加自定义序列化器和反序列化器 this.registerModule(simpleModule); } } ``` ②新建配置类 @Configuration ③实现 WebMvcConfigurationSupport 接口 ④实现 extendMessageConverters 方法 ```java protected void extendMessageConverters(List> converters) { log.info("扩展消息转换器。。"); //创建消息转换器对象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); //设置对象转换器,底层使用Jackson将Java对象转为Json messageConverter.setObjectMapper(new JacksonObjectMapper()); //将上面的消息转换器对象追加到mvc框架的转换器集合中 converters.add(0,messageConverter); } ``` ### 公共字段填充 mybatis plus 公共字段自动填充,插入或更新的时候为指定的字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理 避免重复代码。 实现步骤 1. 在实体类的属性上加入 @TableField 注解,指定自动填充策略 2. 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口 3. 给类加上 @Component 注解 交给springmvc管理 ```java @Component @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { metaObject.setValue("createTime", LocalDateTime.now()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("createUser", 1L); metaObject.setValue("updateUser", 1L); } @Override public void updateFill(MetaObject metaObject) { metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", 1L); } } ``` ### ThreadLocal 什么是ThreadLocal? ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时, ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。 ThreadLocal常用方法: public void set(T value) 设置当前线程的线程局部变量的值 public T get() 返回当前线程所对应的线程局部变量的值 我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id) ,然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id) 使用方法 1. 创建一个类 并 new 一个ThreadLocal对象 set方法写入数据 get方法获取数据 ```java public class BaseContext { private static ThreadLocal threadLocal = new ThreadLocal<>(); public static void set(Long id) { threadLocal.set(id); } public static Long get() { return threadLocal.get(); } } ``` 2. 把用户id存入线程 ```java //将当前用户id存入到当前线程 Long empId = (Long) request.getSession().getAttribute("employee"); BaseContext.set(empId); ``` 3. 从线程中取出用户id ``` //从线程中获取用户的id Long empId = BaseContext.get(); ``` ### 合并 commit 首先假设我们有3个 commit ![image-20220719123805014](http://mml-img.test.upcdn.net//img/202207191238157.png) 我们需要将 `2dfbc7e8` 和 `c4e858b5` 合并成一个 commit,那么我们输入如下命令 ![image-20220719123838499](http://mml-img.test.upcdn.net//img/202207191238539.png) 其中,`-i` 的参数是不需要合并的 commit 的 hash 值,这里指的是第一条 commit, 接着我们就进入到 `vi` 的编辑模式 ![image-20220719123854458](http://mml-img.test.upcdn.net//img/202207191238535.png) 可以看到其中分为两个部分,上方未注释的部分是填写要执行的指令,而下方注释的部分则是指令的提示说明。指令部分中由前方的命令名称、commit hash 和 commit message 组成。 当前我们只要知道 `pick` 和 `squash` 这两个命令即可。 - `pick` 的意思是要会执行这个 commit - `squash` 的意思是这个 commit 会被合并到前一个commit 我们将 `c4e858b5` 这个 commit 前方的命令改成 `squash` 或 `s`,然后输入`:wq`以保存并退出 ![image-20220719123907203](http://mml-img.test.upcdn.net//img/202207191239241.png) 这是我们会看到 commit message 的编辑界面 ![image-20220719123931399](http://mml-img.test.upcdn.net//img/202207191239464.png) 其中, 非注释部分就是两次的 commit message, 你要做的就是将这两个修改成新的 commit message。 ![image-20220719123944787](http://mml-img.test.upcdn.net//img/202207191239842.png) 输入`wq`保存并推出, 再次输入`git log`查看 commit 历史信息,你会发现这两个 commit 已经合并了。 ![image-20220719123953764](http://mml-img.test.upcdn.net//img/202207191239816.png) ### 文件上传 SpringBoot集成了文件上传 可以直接使用 无需在多配置 接受参数类型是 `MultipartFile` file 定义的file名字一定要跟前端传过来的对应 文件名获取 ```java //原始文件名 String originalFilename = file.getOriginalFilename(); ``` 后缀名获取 ```java //截取后缀名 String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); ``` 将临时文件存储到指定位置 ```java file.transferTo(new File()); ``` ### 上传到又拍云 这里的存储路径是以年份+月份+某一天 来确定存储路径 ```java int year = LocalDateTime.now().getYear(); int monthValue = LocalDateTime.now().getMonthValue(); int dayOfMonth = LocalDateTime.now().getDayOfMonth(); ``` 文件名是以UUID随机生成的 防止重复 覆盖掉文件 ```java //使用uuid生成文件名,防止文件名称重复造成覆盖 String filename = year + "/" + monthValue + "/" + dayOfMonth+"/" + UUID.randomUUID().toString() + suffix; ``` 创建又拍云实例 参数为 服务名 操作员 密码 ```java UpYun upyun = new UpYun("mml-tuchuang", "mml520", "HBQEhfMcRxR3KG1yDwEn5DSiQ84MBpcD"); ``` 上传文件 返回一个Boolean 值 ```java boolean result4 = upyun.writeFile(filename, file.getBytes()); ``` 返回数据 存储路径+文件名 从而达到前端进行回显 ### 文件下载 ```java ServletOutputStream outputStream = null; FileInputStream fileInputStream = null; try { //输入流,通过输入流读取文件内容 fileInputStream = new FileInputStream(new File(basePath + name)); //输出流,通过输出流将文件写回浏览器,在浏览器展示图片 outputStream = response.getOutputStream(); response.setContentType("image/jpeg"); int len = 0; byte[] bytes = new byte[1024]; while ((len = fileInputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, len); outputStream.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { assert outputStream != null; outputStream.close(); } catch (IOException e) { e.printStackTrace(); } fileInputStream.close(); } ``` ### DTO DTO ,全程为 Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输 #### Spring Boot的事务管理注解@EnableTransactionManagement的使用 Spring Boot 使用事务非常简单,首先使用注解 @[EnableTransactionManagement ]()开启事务支持后,然后在访问数据库的Service方法上添加注解 @[Transactional]() 便可。 ### 阿里云短信key 请保存或发送 AccessKey 至对应用户。当前窗口关闭后,无法再次查询 Secret。如果您遗失这个 AccessKey,可以创建新的来替代。 AccessKey ID LTAI5tD6vFGQaV1MFH5PPQZ2 AccessKey Secret GzPQTGOgpRowC1q9QBy6GSrgwFDCZm ![image-20220716104236478](http://mml-img.test.upcdn.net//img/202207161042577.png) ### 配置邮件服务 导入依赖包 ``` org.springframework.boot spring-boot-starter-mail ``` 配置 ```java spring: mail: host: smtp.qq.com #发送邮件服务器 username: 2293806028@qq.com #发送邮件的邮箱地址 password: eiikorofnzqsebea #客户端授权码,不是邮箱密码,这个在qq邮箱设置里面自动生成的 port: 587 #端口号465或587 from: ${spring.mail.username} # 发送邮件的地址,和上面username一致 default-encoding: utf-8 # 默认的邮件编码为UTF-8 properties: mail: smtp: socketFactoryClass: javax.net.ssl.SSLSocketFactory #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误 debug: true ``` 发送普通文本邮件 ```java /** * 发送文本邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ public void sendSimpleMail(String to, String subject, String content) { //创建SimpleMailMessage对象 SimpleMailMessage message = new SimpleMailMessage(); //邮件发送人 message.setFrom(from); // 邮件接收人 message.setTo(to); // 邮件主题 message.setSubject(subject); // 邮件内容 message.setText(content); // 发送邮件 mailSender.send(message); } ``` 发送Html邮件 ```java /** * 发送HTML邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ public void sendHtmlMail(String to, String subject, String content) { //获取MimeMessage对象 MimeMessage message = mailSender.createMimeMessage(); try { MimeMessageHelper messageHelper = new MimeMessageHelper(message); //邮件发送人 messageHelper.setFrom(from); //邮件接收人 messageHelper.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容,html格式 第一个参数要发送的内容,第二个参数是不是Html格式。 messageHelper.setText(content, true); //发送时间 messageHelper.setSentDate(new Date()); //发送 mailSender.send(message); //日志信息 log.info("邮件已经发送。"); } catch (MessagingException e) { log.error("发送邮件时发生异常!", e); } } ``` ### Mapper扫包 在SpringBoot项目启动类加上注解 `@MapperScan(basePackages =" 包名")` 就会自动扫包,不用再到Mapper接口中加`@Mapper`注解 ### Linux 查看端口占用情况 lsof lsof(list open files)是一个列出当前系统打开文件的工具。 lsof 查看端口占用语法格式: ``` lsof -i:端口号 ``` 更多 lsof 的命令如下: ``` lsof -i:8080:查看8080端口占用 lsof abc.txt:显示开启文件abc.txt的进程 lsof -c abc:显示abc进程现在打开的文件 lsof -c -p 1234:列出进程号为1234的进程所打开的文件 lsof -g gid:显示归属gid的进程情况 lsof +d /usr/local/:显示目录下被进程开启的文件 lsof +D /usr/local/:同上,但是会搜索目录下的目录,时间较长 lsof -d 4:显示使用fd为4的进程 lsof -i -U:显示所有打开的端口和UNIX domain文件 ``` #### netstat **netstat -tunlp** 用于显示 tcp,udp 的端口和进程等相关情况。 netstat 查看端口占用语法格式: ``` netstat -tunlp | grep 端口号 ``` - -t (tcp) 仅显示tcp相关选项 - -u (udp)仅显示udp相关选项 - -n 拒绝显示别名,能显示数字的全部转化为数字 - -l 仅列出在Listen(监听)的服务状态 - -p 显示建立相关链接的程序名 ### 杀死进程 在查到端口占用的进程后,如果你要杀掉对应的进程可以使用 kill 命令: ``` kill -9 PID ``` ### SpringBoot不同环境使用不同配置文件 **SpringBoot配置文件格式** 在Spring Boot中多环境配置文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识(不一定是.properties文件,也可以是.yml文件)。profile的值,是开发者自定义的,只需要在启动的时候,添加对应的参数,springboot就会去读取该配置文件了。比如我们可以定义为如下格式: ```undefined application-dev.properties:开发环境 application-test.properties:测试环境 application-prod.properties:生产环境 ``` 如果启动的时候,没有指定配置文件,或者指定的配置文件没有对应的项,则会从默认的配置文件中读取。 **默认配置文件为:application.properties(或者application.yml);** **启动默认环境:如果启动时候,没有指定配置文件,就会使用**application.properties这个默认的配置文件; **启动指定环境的方法:启动项目的时候,指定配置文件;** **1. 命令行启动指定** **可以添加-Dspring.profiles.active=的方式指定** 如,指定happy环境:即启动的时候,使用application-happy.properties这个配置文件; ![image-20220717120700908](http://mml-img.test.upcdn.net//img/202207171207043.png) ### nohub命令 > 英文全称 np hang up (不挂起),用于不挂断地运行指定命令。退出终端不会影响程序的运行 语法格式:nohub Command [[Arg...][]] [[&]][] 参数说明: ​ Command:要执行的命令 ​ Arg:一些参数,可以指定输入文件 ​ &:让命令在后台运行 举例:后台运行java -jar命令 ,并将日志文件输入到hello.log文件 ``` nohup java -jar boot工程.jar &>hello.log & ``` ``` nohup java -jar "-Dspring.profiles.active=test" take_out-1.0-SNAPSHOT.jar &>/opt/logs/takeout.log & ``` ### Linux安装git ① `yum list git` 列出git安装包 ![image-20220717124047074](http://mml-img.test.upcdn.net//img/202207171240146.png) ② `yum install git` 在线安装git ![image-20220717124103569](http://mml-img.test.upcdn.net//img/202207171241625.png) ### Linux安装lrzsz 安装方法同上 使用命令 ``` [root@mml xiaohanbao]# rz ``` ![image-20220717134425350](http://mml-img.test.upcdn.net//img/202207171344429.png) ### linux 配置git环境,ssh远程登录. ``` ssh-keygen -t rsa -C "2776856237@qq.com" #邮箱为你的github注册邮箱 ``` 连续三次回车生成shh密钥 ![image-20220717134819056](http://mml-img.test.upcdn.net//img/202207171348171.png) 在~/目录下会出现一个.[ssh]()文件目录. 然后打开这个里面的 `id_rsa.pub ` 文件 ``` [root@mml local]# cat ~/.ssh/id_rsa.pub 复制密钥. ``` 打开自己的github账户.settings -> SSH and GPG keys -> New SSH key ![image-20220717135129840](http://mml-img.test.upcdn.net//img/202207171351977.png) 然后回到终端:输入 ``` eval `ssh-agent` ssh-add ``` 连接测试git ``` ssh -T git@github.com ``` ![image-20220717135236498](http://mml-img.test.upcdn.net//img/202207171352548.png) #### shell 判断文件夹是否存在 ```bash #! /bin/bash path=$1 if [ ! -d $path ];then echo 文件夹不存在 else echo 文件夹存在 fi ``` #### Idea创建springboot没网 ![image-20220717155634454](http://mml-img.test.upcdn.net//img/202207171556534.png) - 第一种解决方法:添加aliyun地址,将默认的地址更改为aliyun地址 **https://start.aliyun.com/** ![image-20220717155707612](http://mml-img.test.upcdn.net//img/202207171557673.png) ### maven缺少依赖包,强制更新依赖命令 maven缺少依赖包,强制更新依赖命令: ```javascript mvn clean install -e -U -Dmaven.test.skip=true ``` 其中 -e 详细异常,-U 强制更新 -DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下。 -Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类。 使用maven.test.skip,不但跳过单元测试的运行,也跳过测试代码的编译。 ```javascript mvn package -Dmaven.test.skip=true ``` ### 自动部署SpringBoot 使用方法 传入三个参数 传入三个参数 程序名字(必填) 部署路径(必填) 环境配置(不填默认就是application.yml) ```bash #! /bin/bash #使用方法 #传入三个参数 程序名字 部署路径 环境配置 echo "==========================" echo "自动化部署脚本启动" echo " =========================" # 要启动的程序名字 APP_NAME=$1 pid=$(ps -ef|grep $APP_NAME|grep 'java -jar'| grep -v grep|awk '{print $2}') # 启动路径 #PATH=$2 # 环境配置 acitve='' if [ $3 ];then if [[ $3 == "dev" ]] || [[ $3 == "test" ]] || [[ $3 == "prod" ]] ;then active='-Dspring.profiles.active='$3 else echo 传入的环境参数错误 echo 例 开发dev 测试test 生产prod exit fi fi if [ $pid ] then echo 程序正在运行 杀死进程 kill -15 $pid fi sleep 2 if [ $pid ] then echo "强制杀死进程" kill -9 $pid else echo "程序停止运行" fi if [ ! -d $2 ] then echo "文件夹不存在 检查传入路径" exit fi echo "准备从Git仓库拉去最新代码" echo $2 cd $2 git pull echo "代码拉取完成" echo "开始打包代码" mvn clean package -Dmaven.test.skip=true echo "打包完成" cd target echo "启动项目" nohup java -jar $active $APP_NAME.jar &>/opt/logs/$APP_NAME.log & echo "项目启动成功" ``` ### Spring Cache ![image-20220717211621330](http://mml-img.test.upcdn.net//img/202207172116665.png) ### Mysql主从复制 ![image-20220718141412382](http://mml-img.test.upcdn.net//img/202207181414606.png) #### 配置-主库Master 第一步:修改mysql数据库的配置文件 /etc/my.cnf [mysqld] log-bin=mysql-bin #[必须]启用二进制日志 server-id=100 #[必须]服务器唯一id ![image-20220718141048131](http://mml-img.test.upcdn.net//img/202207181410275.png) 第二步:重启Mysql服务 `systemctl restart mysqld` 第三步:登录Mysql数据库,执行下面sql `GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@123456';` 注:上面SQL的作用是创建一个用户xiaoming,密码为Root@123456,并且给xiaoming用户授予REPLICATION SLAVE权限。常用于建立复制时所需要用到的用户权限,也就是slave必须被master授权具有该权限的用户,才能通过该用户复制。 第四步:登录mysql数据库,执行下面SQL,记录下结果中File和position的值 `show master status` ![image-20220718143118054](http://mml-img.test.upcdn.net//img/202207181431102.png) #### 配置-从库Slave 第一步:修改mysql数据库的配置文件 /etc/my.cnf [mysqld] server-id=100 #[必须]服务器唯一id 第二步:重启Mysql服务 `systemctl restart mysqld` 第三步:登录Mysql数据库,执行下面sql `change master to master_host='192.168.175.100',master_user='xiaoming',master_password='Root@123456',master_port=3306,master_log_file='mysql-bin.000002',master_log_pos=107;` ![image-20220718143247041](http://mml-img.test.upcdn.net//img/202207181432138.png) 然后执行 启动 slave `start slave;` ![image-20220718143256925](http://mml-img.test.upcdn.net//img/202207181432966.png) 第四步:执行下面SQL,查看数据库的状态 `show slave status;` 太乱了 ![image-20220718143632169](http://mml-img.test.upcdn.net//img/202207181436227.png) 使用 `show slave status\G;` 如果没有问题 标注地方都是yes ![image-20220718152826134](http://mml-img.test.upcdn.net//img/202207181528195.png) 如果出现 错误 Got fatal error 1236 from master when reading data from binary log: 'Could not find first log fil.. ``` Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 1236 Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file' Last_SQL_Errno: 0 ``` ![image-20220718143708759](http://mml-img.test.upcdn.net//img/202207181437816.png) #### 解决办法: ##### 第一步: ```undefined stop slave; reset slave; ``` ##### 第二步: ```ruby mysql> show master status; +------------------+----------+--------------+-------------------------------------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+-------------------------------------------------+-------------------+ | mysql-bin.000001 | 154 | testproxy | mysql,information_schema,performance_schema,sys | | +------------------+----------+--------------+-------------------------------------------------+-------------------+ 1 row in set (0.00 sec) change master to master_host='180.76.123.37',master_user='root',master_password='testprodproxymysql',master_port=3306,master_log_file='mysql-bin.000001',master_log_pos=154; ``` ##### 第三步: ```bash start slave; show slave status \G; # 检查是否均为yes: Slave_IO_Running: Yes Slave_SQL_Running: Yes ``` ### 读写分离 Sharding JDBC ![image-20220718153314552](http://mml-img.test.upcdn.net//img/202207181533827.png) Sharding JDBC 依赖 ``` org.apache.shardingsphere sharding-jdbc-spring-boot-starter 4.0.0-RC1 ``` 配置文件 ``` 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.175.100:3306/rw?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://192.168.175.99:3306/rw?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 ``` 配置好之后就可以了 查询的时候会自动使用从数据源 增删改 会使用主数据源 启动项目报错 The bean 'dataSource', defined in class path resource [org/apache/shardingsphere/shardingjdbc/spring/boot/SpringBootConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/alibaba/druid/spring/boot/autoconfigure/DruidDataSourceAutoConfigure.class] and overriding is disabled. shardingsphere想要创建一个数据源对象 alibaba/druid 也想要创建一个数据源对象 所以发生了冲突 Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true 在配置文件中配置允许bean定义覆盖配置项为 true ``` spring: main: allow-bean-definition-overriding: true ``` 查询操作 ![image-20220718165650301](http://mml-img.test.upcdn.net//img/202207181656440.png) 增删改操作: ![image-20220718165559154](http://mml-img.test.upcdn.net//img/202207181655215.png) ### Nginx #### nginx下载和安装 安装过程: 1、安装依赖包 yum -y install gcc pcre-devel zlib-devel openssl openssl-devel 2、T载Nginx安装包wget https://nginx.org/download/nginx-1.16.1.tar.gz 3、解压 tar -zxvf nginx-1.16.1.tar.gz 4、cd nginx-1.16.1 5、./configure --prefix=/usr/local/nginx 6、 make && make install ![image-20220718180928592](http://mml-img.test.upcdn.net//img/202207181809794.png) 使用tree命令可以看文件夹树形结构 `yum install tree` #### Nginx命令 ##### 查看版本:`./nginx -v` ![image-20220718181350234](http://mml-img.test.upcdn.net//img/202207181813303.png) ##### 检查配置文件的正确性 在启动Nginx服务之前,可以先检查一下conf/nginx.conf文件配置是否有错误 `./nginx -t` ![image-20220718181542539](http://mml-img.test.upcdn.net//img/202207181815623.png) ##### 启动和停止 启动:`./nginx` 停止:`./nginx -s stop` 查看Nginx进程:`ps -ef|grep nginx` ##### 重新加载配置文件 修改Nginx配置文件后,需要重新加载才能生效 命令:`./nginx -s reload` 启动命令太繁琐 可以这样子 修改 `/etc/profile` 文件最后追加 ```bash #Nginx路径 NGINX=/usr/local/nginx/sbin/ export PATH=$NGINX ``` 然后就可在任何地方使用 ![image-20220718190408435](http://mml-img.test.upcdn.net//img/202207181904565.png) #### nginx目录结构 Nginx配置文件(conf/nginx.conf)整体分为三部分: - 全局块 和Nginx运行相关的全局配置. - events块 和网络连接相关的配置 - http块 代理、缓存、日志记录、虚拟主机配置 - http全局块 - Server块 - Server全局块 - location块 注意:http块中可以配置多个Server块,每个Server块中可以配置多个location块。 ![image-20220718192548926](http://mml-img.test.upcdn.net//img/202207181925099.png) ``` server { listen 80; #监听80端口 server_name localhost; #服务名字 location / { #匹配客户端请求的url root html; #静态资源根路径 index index.html index.htm; #默认打开首页 } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } ``` #### 配置反向代理 ``` server { listen 82; #监听80端口 server_name localhost; #服务名字 location / { #匹配客户端请求的url proxy_pass http://192.168.175.99:80 #配置反向代理,将请求转发到指定服务 } } ``` ![image-20220718195931169](http://mml-img.test.upcdn.net//img/202207181959220.png) 反向代理之后的 ![image-20220718200001374](http://mml-img.test.upcdn.net//img/202207182000414.png) #### 负载均衡 ##### 配置负载均衡 默认策略是轮询 #upstream指令可以定义一组服务器 ``` upstream targetserver{ server 192.168.175.99:8083; server 192.168.175.99:8084; server 192.168.175.99:8085; } server{ listen 8080; server_name localhost; location /{ proxy_pass http://targetserver; } } ``` ![image-20220718202045034](http://mml-img.test.upcdn.net//img/202207182020139.png) ![image-20220718202052138](http://mml-img.test.upcdn.net//img/202207182020188.png) ![image-20220718202058178](http://mml-img.test.upcdn.net//img/202207182020237.png) ##### 负载均衡策略 如果不配置策略则 默认配置 轮询 | 名称 | 说明 | | ---------- | ---------------- | | 轮询 | 默认方式 | | weight | 权重方式 | | ip_hash | 依据ip分配方式 | | least_conn | 依据最少连接方式 | | url_hash | 依据url分配方式 | | fair | 依据响应时间方式 | 权重 weight 权重越大 访问的次数越多 ``` upstream targetserver{ server 192.168.175.99:8083 weight=10; server 192.168.175.99:8084 weight=5; server 192.168.175.99:8085 weight=2; } ``` ### linux修改/etc/profile出错command not found解决办法 执行命令 ``` export PATH=/usr/bin:/usr/sbin:/bin:/sbin:/usr/X11R6/bin ``` 因为基本所有的shell命令都包含在上面几个目录中, 执行完后,就可以执行其他命令,赶紧把/etc/profile的错误改正过来~~~ 然后再 ``` #立即生效 source /etc/profile ``` ### Swagger 使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。官网: https://swagger.io/ knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。 #### 使用方式 操作步骤: 1、导入knife4j的maven坐标 ``` com.github.xiaoymin knife4j-spring-boot-starter 3.0.2 ``` 2、导入knife4j相关配置类 在配置类加上这两注解 ``` @EnableSwagger2 @EnableKnife4j ``` 创建一个Docket对象 ```java @Bean public Docket createRestApi(){ //文档类型 return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.takeout.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("小憨包外卖") .version("1.0") .description("小憨包接口文档") .build(); } ``` 3、设置静态资源,否则接口文档页面无法访问 在原先的配置静态资源映射的方法中添加这两个 ```java registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars"); ``` 4、在LoginCheckFilter中设置不需要处理的请求路径 ![image-20220718215931978](http://mml-img.test.upcdn.net//img/202207182159215.png) ![image-20220718215953262](http://mml-img.test.upcdn.net//img/202207182159370.png) #### 常用注解 | 注解 | 说明 | | ------------------ | -------------------------------------------------------- | | @Api | 用在请求的类上,例如Controller,表示对类的说明 | | @APiModel | 用在类上,通常是实体类,表示一个返回响应数据的通信 | | @ApiModeProperty | 用在属性上,描述响应类的属性 | | @ApiOperation | 用在请求的方法上,说明方法的用途,作用 | | @ApiimplicitParams | 用在请求的方法上,表示一组参数说明 | | @ApiimplicitParam | 用在@ApiimplicitParams注解中,指定一个请求参数的各个方面 |