1.Swagger
1.1Swagger简介
前后端分离:
Vue+Spring boot
- 前端 -> 前端控制层、视图层
- 伪造后端数据,json就不需要后端项目依旧能够跑起来
- 后端 -> 后端控制层、服务层、数据访问层
- 前后端通过API进行交互
- 前后端分离模式相对独立、且松耦合
产生的问题:
- 前后端集成,前端或者后端无法做到“及时协商,尽早解决”,最终导致问题集中爆发
解决方案:
- 首先定义schema(计划的提纲 ),并实时跟踪最新的API,降低集成风险
- 早些年用word计划文档
- 前后端分离:
- 前段测试后端接口:postman
- 后端提供接口,需要实时更新最新的消息和改动
Swagger:
Swagger是一个规范和完整的框架,是一个可以根据你的代码,自动生成接口文档的一个工具,并且可以用作接口测试工具。
- 号称世界上最流行的API框架
- Restful Api 文档在线自动生成器 => API 文档 与API 定义同步更新
- 直接运行,在线测试API
- 支持多种语言 (如:Java,PHP等)
- 官网:https://swagger.io/
1.2SpringBoot集成
1.搭建项目,并测试,确保能运行
@Controller
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello";
}
}
2.项目中使用Swagger需要导入启动器:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
也可以导入两个jar包依赖:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
3.编写配置类,运行访问:http://localhost:8080/swagger-ui/index.html
@Configuration
@EnableSwagger2//开启swagger
public class SwaggerConfig {
}
注意:
这里会报错解决方法----启动类添加注解@EnableWebMvc:
-
当使用@EnableWebMvc时,加载的是WebMvcConfigurationSupport中的配置项
-
当不使用@EnableWebMvc时,使用的是WebMvcAutoConfiguration引入的配置项
测试结果:
1.3swagger配置
Swagger的实例Bean是Docket,所以通过配置Docket实例来配置Swaggger:
@Configuration
@EnableSwagger2//开启swagger
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
//配置swagger的信息
public ApiInfo apiInfo(){
//作者信息
Contact DEFAULT_CONTACT = new Contact("容辞", "https://mp.csdn.net/mp_blog/manage/article?spm=1010.2135.3001.5416", "3521945255@qq.com");
return new ApiInfo(
"容辞API",
"即使再小的帆也能远航",
"v1.0",
"https://mp.csdn.net/mp_blog/manage/article?spm=1010.2135.3001.5416",
DEFAULT_CONTACT,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList<>());
}
}
1.4配置扫描接口
-
构建Docket时通过select()方法配置怎么扫描接口
-
RequestHandlerSelectors配置要扫描接口的方式
- basePackage():要扫描的包
- any():扫描所有,项目中的所有接口都会被扫描到
- none():不扫描接口
- withMethodAnnotation(GetMapping.class):通过方法上的注解扫描,只扫描get请求
- withMethodAnnotation(Controller.class):通过类上的注解扫描,只扫描有controller注解的类中的接口
测试结果:
apis:
paths:
1.5开关
通过 enable() 方法配置是否启用swagger,如果是false,swagger将不能在浏览器中访问了
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.enable(false);
}
动态配置当项目处于test、dev(生产)环境时显示swagger,处于prod(发布)时不显示。
多配置文件:
spring.profiles.active=dev
@Bean
public Docket docket(Environment environment){
//设置要显示的swagger环境
Profiles profiles = Profiles.of("dev");
//通过环境监听变量,判断是否处于自己设置的环境之中
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.enable(flag);
}
测试:
1.6API文档分组
如果没有配置分组,默认是default;通过groupName()方法即可配置分组:
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("A");
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2).groupName("B");
}
@Bean
public Docket docket3(){
return new Docket(DocumentationType.SWAGGER_2).groupName("C");
}
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).groupName("容辞");
}
1.7扫描实体类
只要这个实体类在请求接口的返回值上,都能被扫描到swagger中:
@ApiModel("实体类")
public class User {
@ApiModelProperty("用户名")
public String userName;
@ApiModelProperty("密码")
public String userPwd;
}
@PostMapping("/u")
@ResponseBody
public User u(){
return new User();
}
为实体添加注释:
-
@ApiModel为类添加注释
-
@ApiModelProperty为类属性添加注释
测试结果:
扩展注解:
Swagger注解 | 简单说明 |
---|---|
@Api(tags = “模块说明”) | 作用在模块类上 |
@ApiOperation(“接口说明”) | 作用在接口方法上 |
@ApiModel(“POJO说明”) | 作用在模型类上:如VO、BO |
@ApiModelProperty(value = “属性说明”,hidden = true) | 作用在类方法和属性上,hidden设置为true可以隐藏该属性 |
@ApiParam(“参数说明”) | 作用在参数、方法和字段上,类似@ApiModelProperty |
@ApiOperation("hello控制类")
@GetMapping("/hello1")
@ResponseBody
public String hello1(@ApiParam("姓名") String name){
return "hello"+name;
}
测试结果:
扩展皮肤:
我们可以导入不同的包实现不同的皮肤定义
-
bootstrap-ui----访问 http://localhost:8080/doc.html
<!-- 引入swagger-bootstrap-ui包 /doc.html--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency>
-
Layui-ui----访问 http://localhost:8080/docs.html
<!-- 引入swagger-ui-layer包 /docs.html--> <dependency> <groupId>com.github.caspar-chen</groupId> <artifactId>swagger-ui-layer</artifactId> <version>1.1.3</version> </dependency>
-
knife4j----访问http://localhost:8080/doc.html
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency>
总结:
- 我们可以通过swagger给一些比较难理解的属性或者接口增加注释信息
- 接口文档实时更新
- 可以在线测试
- 正是大部的时候一定要关闭swagger,出于安全考虑也是节省运行内存
2.任务
21.异步任务
我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。
1.AsyncService类
@Service
public class AsyncService {
public void hello(){
try {
Thread.sleep(3000);//等3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("业务进行中");
}
}
2.AsyncController类
@RestController
public class AsyncController {
@Autowired
AsyncService asyncService;
@RequestMapping("/hello")
public String hello(){
asyncService.hello();
return "success";
}
}
我们如果想让用户直接得到消息,就在后台使用多线程的方式进行处理即可,但是每次都需要自己手动去编写多线程的实现的话,太麻烦了,我们只需要用一个简单的办法,在我们的方法上加一个简单的注解即可:
- @EnableAsync—启动类上
- @Async-------------方法上
2.2定时任务
2.2.1简介
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了两个接口:
- TaskScheduler接口 任务调度者
- TaskExecutor接口 任务执行者
两个注解:
- @EnableScheduling–用在启动类上
- @Scheduled–用在方法上,定时任务在什么时候执行
@Service
public class ScheduledService {
//在一个特定的时间去执行这个方法
//每天的晚上18:14:30都会执行
@Scheduled(cron = "30 14 20 * * *")
public void hello(){
System.out.println("hello你被执行了");
}
}
2.2.2cron
cron计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思。在Linux中,我们经常用到 cron 服务器来完成这项工作。
cron的格式:S M H D m d
S:秒(0-59)
M: 分钟(0-59)
H:小时(0-23)
D:日(1-31)
m: 月(1-12)
d: 星期(1~7,1为星期天)
每个符号的意义:
* 表示所有值
? 表示未说明的值,即不关心它为何值
- 表示一个指定的范围
, 表示附加一个可能值
/ 符号前表示开始时间,符号后表示每次递增的值;
L last
# 用来指定这个月的第几个周几
一些cron表达式案例:
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
2.3邮件任务
-
邮件发送需要引入spring-boot-start-mail
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
SpringBoot 自动配置MailSenderAutoConfiguration(源码)
-
定义MailProperties内容,配置在application.properties中
spring.mail.host=smtp.qq.com spring.mail.password=kdxhvcsofkuycjef spring.mail.username=3521945255@qq.com #开启加密验证 spring.mail.properties.mail.smtp.ssl.enable=true
-
自动装配JavaMailSenderImpl
@Autowired JavaMailSenderImpl javaMailSender; //简单邮件 @Test void contextLoads() { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject("springboot邮件发送"); mailMessage.setText("邮件任务测试"); mailMessage.setTo("3521945255@qq.com"); mailMessage.setFrom("3521945255@qq.com"); javaMailSender.send(mailMessage); } //复杂邮件 @Test void contextLoads1() throws MessagingException { MimeMessage mimeMessage = javaMailSender.createMimeMessage(); //组装 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true); helper.setSubject("springboot复杂邮件"); helper.setText("<h1>内容</h1>",true); helper.addAttachment("1.jpg",new File("D:\\壁纸\\wallhaven-nz67rg.jpg")); helper.setTo("3521945255@qq.com"); helper.setFrom("3521945255@qq.com"); javaMailSender.send(mimeMessage); }
3.分布式
3.1分布式系统
分布式系统是 若干独立计算机的集合 ,这些计算机对于用户来说就像单个相关系统。
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
分布式系统(distributed system)是建立在网络之上的软件系统。
首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构(网络可能会不可靠–崩掉),会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。
3.2Dubbo文档
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,急需一个 治理系统 确保架构有条不紊的演进。
1.单一应用架构:
适用于小型网站,将所有功能都部署到一个功能里,简单易用,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
缺点:性能扩展比较难,协同开发问题,不利于升级维护。
2.垂直应用架构:
当访问量逐渐增大,通过切分业务来实现各个模块独立部署,以提升效率,性能扩展也更方便。此时,用于加速前端页面开发的Web框架(MVC)是关键。
缺点:公用模块无法重复利用,开发性的浪费。
3.分布式服务架构:
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
4.流动计算架构:
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心,提高资源利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键。
3.3RPC
RPC(Remote Procedure Call)是指 远程过程调用 ,是一种进程间通信方式(类似HTTP),他是一种技术的思想,而不是规范。它允许程序调用另一台机器上的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
RPC基本原理:RPC两个核心模块:通讯,序列化。(序列化:数据传输需要转换)
推荐阅读文章:https://www.jianshu.com/p/2accc2840a1b
4.Dubbo
4.1简介
Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架(Dubbo用来实现RPC),它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
dubbo官网 :https://dubbo.apache.org/zh/index.html
dubbo文档(进入更快):https://dubbo.gitbooks.io/dubbo-user-book/content/preface/background.html
dubbo架构:
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长链接推送变更数据给消费者。
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
4.2Zookeeper安装
- ZooKeeper是一个分布式服务框架,是一个树型的目录服务,是Apache Hadoop 的一个子项目,,可以用ZooKeeper来做:统一配置管理、统一命名服务、分布式锁、集群管理。
- 适合作为 Dubbo 服务的注册中心
- 使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够通用解决这些问题的中间件就应运而生了。
- 简单来说zookeeper=文件系统+监听通知机制
zookeeper下载地址:http://archive.apache.org/dist/zookeeper/
配置:
-
运行/bin/zkServer.cmd ,初次运行可能遇到问题闪退 !解决方案:编辑zkServer.cmd文件末尾添加pause 。这样运行出错就不会退出,会提示错误信息,方便找到原因。
-
将 conf 目录下的 zoo_sample.cfg 文件,复制一份,重命名为 zoo.cfg
-
在安装目录下面新建一个空的 data 文件夹和 log 文件夹
-
修改 zoo.cfg 配置文件,将 dataDir=/tmp/zookeeper 修改成 zookeeper 安装目录所在的 data 文件夹,再添加一条添加数据日志的配置
-
双击 zkCli.cmd 启动程序(zkServer.cmd也是启动状态)
-
测试:
-
扩展:ZooKeeper audit is disabled:
zookeeper新版本启动的过程中,zookeeper新增的审核日志是默认关闭,所以控制台输出ZooKeeper audit is disabled,标准的修改方式应该是在zookeeper的配置文件zoo.cfg新增一行
audit.enable=true
即可
4.3dubbo-admin安装
dubbo本身并不是一个服务软件,它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。
为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不过这个监控即使不装也不影响使用。
1.下载dubbo-admin地址 :
https://github.com/apache/dubbo-admin/releases
https://github.com/apache/dubbo-admin
2.修改端口号D:\java文件\dubbo-admin-develop\dubbo-admin-server\src\main\resources
3.在项目目录下打包dubbo-admin:
mvn clean package -Dmaven.test.skip=true
第一次打包的过程有点慢,需要耐心等待,直到成功,出现BUILD SUCCESS!
4.执行目录D:\java文件\dubbo-admin-develop\dubbo-admin-server\target下的jar包
java -jar dubbo-admin-server-0.4.0.jar
此时zookeeper的服务zkServer.cmd一定要打开。
5.报错:
java.io.IOException: 远程主机强迫关闭了一个现有的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:68)
at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:366)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1145)
出现这个的原因是zookeeper客户端连接数太多,默认最大连接数是60
解决办法:修改zookeeper的配置文件zoo.cfg,添加参数:maxClientCnxns=200
6.测试:我们去访问一下 http://localhost:7001/ , 这时候我们需要输入登录账户和密码,我们都是默认的root、root,成功访问!
4.4SpringBoot + Dubbo + zookeeper
Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展进行加载。
4.4.1provider-server
1.新建一个空项目,添加模块springboot,实现服务提供者:provider-server
public interface TicketService {
public String getTicket();
}
@org.apache.dubbo.config.annotation.Service//服务注册与发现,在项目启动的时候自动注入到注册中心
@Component//不用service的原因是dubbo也是service容易导错包
public class TicketServiceImpl implements TicketService {
@Override
public String getTicket() {
return "啦啦啦啦啦啦";
}
}
2.导入依赖:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!--zookeeper及其依赖包,解决日志冲突,还需要剔除日志依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<!--排除这个slf4j-log4j12-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
3.在springboot配置文件中配置dubbo相关属性
server.port=8081
#服务应用的名字
dubbo.application.name=provider-server
#注册中的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下的服务
dubbo.scan.base-packages=com.moli.service
dubbo.protocol.port=20881
4.运行provider,打开zookeeper的zkServer.cmd ,可以打开监控器dubbo-admin查看是否注入到注册中心(这两项工作最好在项目运行前解决)。
已经监控到数据:
5.如果无法显示元数据,就在服务提供端添加元数据配置类
import org.apache.dubbo.config.MetadataReportConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DubboConfig {
@Bean
public MetadataReportConfig metadataReportConfig() {
MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
metadataReportConfig.setAddress("zookeeper://127.0.0.1:2181");
return metadataReportConfig;
}
}
4.4.2consumer-server
1.添加模块,实现服务消费者:consumer-server,导入依赖同provider
2.UserService
@Service//注册到容器中
public class UserService {
//我们需要拿注册中心的服务
@Reference//dubbo远程引用:pom坐标;或者可以定义路径相同的接口名----生产者的TicketService
TicketService ticketService;
public void buyTicket(){
String ticket = ticketService.getTicket();
System.out.println("在注册中心拿到"+ticket);
}
}
本来正常步骤是需要将服务提供者的接口打包,然后用pom文件导入;我们这里使用简单的方式,直接将服务的接口拿过来,路径必须保证正确,即和服务提供者相同。
3.测试
@Autowired
UserService userService;
@Test
void contextLoads() {
userService.buyTicket();
}
4.4.3小结
步骤:
前提:zookeeper服务开启
- 提供者
- 导入依赖
- 配置注册中心地址,服务者发现名,要扫描的包
- 在想要被注册的服务上面加一个注解@Service
- 消费者
- 导入依赖
- 配置注册中心地址,自己的服务名
- 从远程注入服务加个注解@Reference
扩展:
强制关闭端口:
#根据端口号查询这个端口号的PID
netstat -ano|findstr "端口号"
#根据查出来的pid 强制关闭这个端口号
taskkill -F -PID 查出的号