SpringBoot进阶应用
封面画师:adsuger 封面ID:79185730
1. Swagger
1.1 Swagger简介
在学习Swagger之前,我们需要先了解 前后端分离 :
前后端分离:
- 后端:后端控制层,服务层,数据访问层
- 前端:前端控制层,视图层
- 使用JSON,伪造后端数据。因为伪造了数据,就不需要后端数据,前端工程就可以运行起来
- 前后端如何交互?使用API接口
- 前后端相对独立,实现低耦合
- 一个项目的前后端甚至可以部署在不同的服务器上
使用了前后端分离,就会产生一个问题:前端人员与后端人员无法做到“及时协商,尽早解决”,最终导致问题集中式爆发。(就知道变需求,变变变,变NM,给爷死!😡)
这个时候就需要一种解决方法:
- 首先指定Schema【计划的提纲】,实时更新最新API,降低集成的风险
- 早些年:指定Word计划文档
- 在前后端分离时代:
- 前端测试后端接口:postman
- 后端提供接口,需要实时更新最新的消息及改动!
这个时候,一个帅气的“男人登场了”——Swagger。😂
Swagger
- Swagger号称世界上最流行的API框架
- RestFul API 文档在线生成 ,实现 API 文档 与API 定义同步更新
- 直接运行,在线测试API
- 支持多种语言 (如:Java,PHP …)
- 官网地址:Swagger
1.2 SpringBoot集成Swagger
在项目中使用Swagger需要springfox:
- swagger2
- swagger-ui
使用步骤
在创建的SpringBoot项目中导入下列依赖:
1 | <!-- springfox-swagger-ui --> |
编写一个简单的Controller,如:HelloController,确保页面能够正确跳转。
1 |
|
编写配置类SwaggerConfig
用于配置Swagger:
1 |
|
然后访问:http://localhost:8080/swagger-ui.html
,就可以看到Swagger的界面。
1.3 配置Swagger
Swagger实例Bean是Docket,所以通过配置Docket实例来配置Swaggger,在配置类SwaggerConfig
中编写:
1 |
|
通过apiInfo()
属性配置文档信息:
1 | //配置swagger信息=apiInfo |
然后将Docket实例关联apiInfo()
:
1 |
|
然后我们可以再次运行项目,访问:http://localhost:8080/swagger-ui.html
,再次查看Swagger的界面:
1.4 配置扫描接口及开关
现在有一个需求:我只希望我的Swagger在生产环境中使用,在发布的时候不使用。
解决方法:
- 判断当前环境是否是生产环境 flag = false
- 注入enable (flag)
编写配置文件application-dev.properties
:
1 | server.port=8080 |
编写配置文件application-pro.properties
:
1 | server.port=8083 |
编写主配置文件application.properties
:
1 | # 激活项目某个环境 |
修改Docket实例:
1 | //配置Swagger的Docket的bean实例 |
1.5 配置API分组
配置单个API分组
只需要在Docket实例返回值中添加以下代码即可:
1 | .groupName("Yang") |
配置多个API分组
只需要在配置类SwaggerConfig
中创建多个Docket实例并添加至容器即可:
1 |
|
然后我们可以运行项目,访问:http://localhost:8080/swagger-ui.html
,查看Swagger的界面:
1.6 配置实体
我们发现在界面中,我们的 Models 一项还没用到,Models可以用来配置实体。
配置实体方式
新建一个实体类:
1 | //@Api(注释) |
只需要请求接口的返回值是实体类型(即使是泛型),都能映射到 Models 中。在Controller中进行编写:
1 | //只要我们的接口中,返回值存在实体类,它就会呗扫描到swagger中 |
然后我们重启项目,访问:http://localhost:8080/swagger-ui.html
,查看Swagger的界面并点开 Models 项:
注意:并不是因为@ApiModel
这个注解让实体显示在 Models 里了,而是出现在接口方法的返回值上的实体都会显示在这里,而@ApiModel
和@ApiModelProperty
这两个注解只是为实体添加注释说明。
@ApiModel
为类添加注释说明
@ApiModelProperty
为类属性添加注释说明
这里还有个坑! 在实体类中整型、浮点型属性使用@ApiModelProperty
注解时,记得给注解加属性example
,否则会报错:java.lang.NumberFormatException: empty String
。虽然不影响Swagger的运行,但是控制台打印个错误还是不爽。😂
1.7 常用注释
Swagger的注释可用于说明某个属性、方法、类、参数:
注解 | 注解位置 |
---|---|
@Api | 作用在模块类上 |
@ApiOperation | 作用在接口方法上 |
@ApiModel | 作用在模型类上:如VO、BO |
@ApiModelProperty | 作用在类方法和属性上,hidden属性设置为true可以隐藏该属性 |
@ApiParam | 作用在参数、方法和字段上,类似@ApiModelProperty |
在控制类中使用注解:
1 | public class HelloController { |
hello2注解说明:
1.8 设置Swagger皮肤
我们在最初使用Swagger时,导入了Swagger默认UI的依赖:
1 | <dependency> |
这时候,UI的风格是Swagger的默认风格。我们可以导入不同的依赖来更换不同的主题。
Bootstrap风格:
1 | <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui --> |
LayerUI风格:
1 | <!-- https://mvnrepository.com/artifact/com.github.caspar-chen/swagger-ui-layer --> |
我们只需要在 Maven仓库 中搜索关键词swagger-ui
,就可以找到很多的主题了。😎
2. 任务
2.1 异步任务
所谓异步任务,就是异步处理的任务。比如,我们在网站上发邮件,后台就会执行发邮件的逻辑,但是发邮件这个操作需要一定的时间,不是立刻就发送成功的,在这期间,如果不使用异步处理,就会造成前台无响应,直到邮件发送成功,前台才有响应,这是十分不友好的。因此,我们对于一些任务需要进行异步处理,即:采用多线程的方式来处理这些任务。
异步任务操作
创建一个SpringBoot项目,在项目中创建一个名为service的包。
在service包下创建一个名为AsyncService
的类。
在AsyncService
中编写一个方法,给线程进行休眠,模拟业务响应:
1 |
|
然后再创建一个controller包,在包下创建一个名为AsyncController
的控制类:
1 |
|
启动程序,访问http://localhost:8080/hello
。这时,网页会卡顿三秒(转圈三秒),然后跳转界面并显示 “OK” 在界面上。
上面模拟的是同步等待的情况,在实际业务中,我们想让用户直接得到信息,可以在后台使用多线程的方法,但是每次自己手写显得过于麻烦,因此,SpringBoot给了我们一个封装的注解@Async
。
给业务处理方法加上注解@Async
:
1 |
|
还需要在主启动类上加上一个异步的注解@EnableAsync
:
1 |
|
最后,我们再运行项目,访问http://localhost:8080/hello
。这个时候,界面不会出现卡顿,而是直接跳转至消息界面,所对应的,后台正在进行处理:三秒后,控制台打印数据处理完毕
。
2.2 邮件发送
引入依赖:
1 | <!--javax.mail:配置--> |
从依赖名我们可以看出,这是SpringBoot官方的启动器。我们可以先点进去看一下,在这个依赖中可以看到:
1 | <dependency> |
这个依赖就是用来完成邮件发送的。
除此之外,我们可以全局搜索MailAutoConfiguration
,看看SpringBoot有没有编写一个自动配置类。果不其然,我们发现了一个名为MailSenderAutoConfiguration
的自动配置类。在这个类中,我们并没有发现注册的bean,我们可以看看它导入的类中有没有。点击导入的第一个类:
进入类后:
然后,我们可以看一下配置文件MailProperties
:
1 |
|
通过阅读代码,我们得知:我们可以在主配置文件中进行关于邮件发送的配置,然后SpringBoot就会自动进行装配。
我们在此使用QQ邮箱做示范。为了不让我们的密码直接暴露在代码前,我们需要开启POP3和SMTP服务以获取授权码,进入QQ邮箱后,设置 – 账户 – 开启服务:
然后,我们在主配置文件中进行配置:
1 | spring.mail.username=cy.mofan@qq.com |
最后,我们可以在测试类中编写测试代码:
1 |
|
因为要测试发送邮件,所以 得联网 才能测试成功。
2.3 定时任务
所谓定时任务,就是在规定的时间上自动执行某个操作。比如:每天凌晨的时候,输出并分析前一天的日志信息,这就需要定时任务来完成。SpringBoot为我们异步执行任务调度提供了两个接口:
- TaskExecutor接口 -----> 任务调度者
- TaskScheduler接口 -----> 任务执行程序
同时,还提供了两个注解:
@EnableScheduling
(表示开启定时功能的注解,位置在主启动类上)@Scheduled
(表示任务什么时候执行)
在@Scheduled
中,属性需要使用Cron表达式,来表示任务什么时候执行。
Cron表达式:Cron表达式在线工具
定时任务的使用
首先在service包下创建一个类ScheduledService
,在其中编写一个方法,这个方法需要定时执行:
1 |
|
然后在主启动类上使用@EnableScheduling
注解:
1 |
|
3. 分布式系统
3.1 分布式系统理论
简单介绍一下分布式系统
《分布式系统原理与范式》一书中有:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”。
所谓分布式系统,就是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是 利用更多的机器,处理更多的数据 。
分布式系统是建立在网络之上的软件系统。
什么时候使用分布式?
只有当单个节点无法满足我们的需求,且硬件的提升已经远超过所带来的利益时才使用分布式系统。简单的、个人的系统,不用使用分布式系统,因为使用了分布式系统会引进很多单机系统没有的问题,为了解决这些问题需要已经各种协议或机制,从而带来更多的问题…
Dubbo文档
Dubbo官网:Dubbo
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
3.2 RPC
参考链接:RPC原理解析 、 你应该知道的RPC原理
RPC【Remote Procedure Call】:远程过程调用,是一种进程间通信方式,是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。RPC就是要像调用本地的函数一样去调远程函数。
RPC的两个核心模块:通讯、序列化。
RPC基本原理
步骤解析
参考链接:Dubbo-
(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;
(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
(3) 客户端通过sockets将消息发送到服务端;
(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);
(5) 服务端存根( server stub)根据解码结果调用本地的服务;
(6) 本地服务执行并将结果返回给服务端存根( server stub);
(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
(8) 服务端(server)通过sockets将消息发送到客户端;
(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
(10) 客户端(client)得到最终结果。
RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来。
注意:无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。
3.3 搭建测试环境
Dubbo
Dubbo |ˈdʌbəʊ| 官网:Dubbo
Dubbo运行原理图:
服务提供者(Provider) :暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer) :调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
监控中心(Monitor) :服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Windows下安装ZK
下载地址:清华镜像下载 选择版本后,下载并解压。
解压完成后,进入解压的ZK目录下,再进入bin目录,运行zkServer.cmd
,这个时候一般会闪退。我们可以编辑zkServer.cmd文件,在末尾处添加pause
。
这样出错后就不会直接闪退,而是会打印错误信息:
根据错误信息,我们得知:闪退是因为缺少zoo.cfg
文件造成的。
前往conf目录,将zoo_sample.cfg
复制一份在当前目录,然后改名为zoo.cfg
。
在这个文件中我们可以看到以下信息:
1 | tickTime=2000 |
完成简单的修改后,我们在启动服务。双击运行zkServer.cmd
:
表示启动成功!
然后我们运行zkCli.cmd
,启动客户端。
注意:一定要先启动服务,并且保持服务的黑窗口一直运行!
1 | WATCHER:: |
启动客户端后可能会出现上述情况,我们点击回车即可。
启动后,在客户端查看一下所有节点:
1 | ls / |
我们还可以自己创建一个节点:
1 | 创建一个名为mofan,值为yang的节点 |
获取创建节点的值:
1 | get /mofan |
最后,在查看一下所有节点,输入命令ls /
,这时候会发现我们创建的节点也会显示出来。
安装dubbo-admin
Dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的Java程序连接到ZooKeeper,并利用ZooKeeper消费、提供服务。
为了让用户更好的管理监控众多的Dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,dubbo-admin只是一个后台监控系统,因此,也可以不使用它。
下载地址:Dubbo下载 (下载时,一般选择下载主节点,即:master节点的文件)
如果使用直接下载zip文件,而非Git clone下载,这时候需要解压文件。
解压文件后,进入dubbo-admin-master/dubbo-admin-master/dubbo-admin/src/main/resources,然后找到application.properties
文件:
1 | server.port=7001 |
如果你修改了ZK的地址端口,这里也需要修改!
然后我们需要在 项目目录下 打包dubbo-admin,使用命令行,运行:
1 | mvn clean package -Dmaven.test.skip=true |
先启动ZK的服务!一定要先启动,因为dubbo依赖ZK!
再执行 dubbo-admin\target 下的dubbo-admin-xxx.jar:
1 | java -jar dubbo-admin-xxx.jar |
运行完成后,我们在浏览器地址栏输入http://localhost:7001/
,再输入账号(root)和密码(root),就可以进入dubbo-admin的界面了:
至于为什么是7001端口,账号密码为什么都是root的原因也很简单,还记得前面说的application.properties
文件吗?如果你想要改,在这里配置文件中修改就可以了。
4. SpringBoot-Dubbo-ZK
4.1 搭建架构
使用IDEA创建一个空项目,然后在项目中创建一个SpringBoot模块,选中Web依赖,取名为provider-server
表示服务提供者模块。
然后导入依赖:
1 | <!--导入依赖:Dubbo ZK--> |
创建一个service包,在包下创建TicketService
接口:
1 | public interface TicketService { |
再在包下创建接口的实现类TicketServiceImpl
:
1 | package com.yang.service; |
我为什么要将导入的包也复制进来? 注意此处的@Service
注解是Dubbo的。
在配置文件中进行配置:
1 | server.port=8001 |
如果不在配置文件中设置 dubbo.scan.base-packages
,那么就要在主启动类上加上注解 @EnableDubbo
,开启基于注解的 Dubbo 功能。
最后,可以进行测试!
先 打开ZK服务 ,然后运行provider-server
模块,运行完之后我们应该怎么看服务被注册呢?我们可以启动dubbo-admin,进监控后台查看。点击导航栏的 服务治理 — 提供者:
点击机器IP:
4.2 服务消费者
完成架构搭建后,不要关闭ZK,不要关闭dubbo-admin,不要关闭提供者程序!
完成架构搭建后,不要关闭ZK,不要关闭dubbo-admin,不要关闭提供者程序!
完成架构搭建后,不要关闭ZK,不要关闭dubbo-admin,不要关闭提供者程序!
重要的事情说三遍!
首先,在项目中再创建一个SpringBoot模块,导入Web依赖,命名为consumer-server
,表示消费者服务。
导入与提供者相同的依赖。
创建service包,将提供者的TicketService
接口复制一份到这个包下,保证这个接口在消费者服务的目录结构与提供者的目录结构相同,方便待会使用。
再在包下创建一个UserService
类:
在这个类中编写代码:
1 | package com.yang.service; |
我为什么要将导入的包也复制进来? 注意此处的@Service
注解是Spring的。
在配置文件中进行配置:
1 | server.port=8002 |
编写测试类进行测试:
1 |
|
然后,运行consumer-server
模块,在控制台中查看输出: