封面画师: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 项目中导入下列依赖:
<!-- springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
编写一个简单的 Controller,如:HelloController,确保页面能够正确跳转。
@RestController
public class HelloController {
@GetMapping(value = "/hello")
public String hello(){
return "hello";
}
}
编写配置类 SwaggerConfig 用于配置 Swagger:
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig {
}
然后访问:http://localhost:8080/swagger-ui.html,就可以看到 Swagger 的界面。

1.3 配置 Swagger
Swagger 实例 Bean 是 Docket,所以通过配置 Docket 实例来配置 Swaggger,在配置类 SwaggerConfig 中编写:
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2);
}
通过 apiInfo() 属性配置文档信息:
//配置swagger信息=apiInfo
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("默烦", "https://space.bilibili.com/23658864", "cy.mofan@qq.com");
return new ApiInfo(
"Yang的Swagger日记",
"中二少年欢乐多!",
"v1.0",
"https://space.bilibili.com/23658864",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList()
);
}
然后将 Docket 实例关联 apiInfo():
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
然后我们可以再次运行项目,访问:http://localhost:8080/swagger-ui.html,再次查看 Swagger 的界面:

1.4 配置扫描接口及开关
现在有一个需求:我只希望我的 Swagger 在生产环境中使用,在发布的时候不使用。
解决方法:
- 判断当前环境是否是生产环境 flag = false
- 注入 enable (flag)
编写配置文件 application-dev.properties:
server.port=8080
编写配置文件 application-pro.properties:
server.port=8083
编写主配置文件 application.properties:
# 激活项目某个环境
spring.profiles.active=dev
修改 Docket 实例:
//配置Swagger的Docket的bean实例
@Bean
public Docket docket(Environment environment){
//设置要显示的swagger环境
Profiles profiles = Profiles.of("dev","test");
//获取项目环境:spring.profiles.active=dev
//通过environment.acceptsProfiles判断是否处在自己设定的环境中
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(flag) //enable() 是否启动swagger,若为false,则swagger不能在浏览器中访问
.select()
//RequestHandlerSelectors 配置要扫描接口的方式
//basePackage 指定要扫描的包
//any():扫描全部
//none():都不扫描
//withClassAnnotation():扫描类上的注解,参数是一个注解的反射对象,eg:RestController.class
//withMethodAnnotation():扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("com.yang.swagger"))
//path() 过滤的路径
//.paths(PathSelectors.ant("/yang/**"))
.build();
}
1.5 配置 API 分组
配置单个 API 分组
只需要在 Docket 实例返回值中添加以下代码即可:
.groupName("Yang")
配置多个 API 分组
只需要在配置类 SwaggerConfig 中创建多个 Docket 实例并添加至容器即可:
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("AAA");
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2).groupName("BBB");
}
@Bean
public Docket docket3(){
return new Docket(DocumentationType.SWAGGER_2).groupName("CCC");
}
然后我们可以运行项目,访问:http://localhost:8080/swagger-ui.html,查看 Swagger 的界面:

1.6 配置实体
我们发现在界面中,我们的 Models 一项还没用到,Models 可以用来配置实体。
配置实体方式
新建一个实体类:
//@Api(注释)
@ApiModel("账户实体类")
public class Account {
@ApiModelProperty("用户名")
public String username;
@ApiModelProperty(value = "余额",example = "1.50")
public float money;
@ApiModelProperty("密码")
public String password;
}
只需要 请求接口 的返回值是实体类型(即使是泛型),都能映射到 Models 中。在 Controller 中进行编写:
//只要我们的接口中,返回值存在实体类,它就会呗扫描到swagger中
@PostMapping(value = "/account")
public Account account(){
return new Account();
}
然后我们重启项目,访问: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 |
在控制类中使用注解:
public class HelloController {
// ...
//Operation接口
@ApiOperation("Hello控制类")
@GetMapping(value = "/hello2")
public String hello2(@ApiParam("用户名") String username){
return "hello"+username;
}
@ApiOperation("post测试类")
@PostMapping(value = "/postt")
public Account postt(@ApiParam("用户名") Account account){
return account;
}
}
hello2 注解说明:

1.8 设置 Swagger 皮肤
我们在最初使用 Swagger 时,导入了 Swagger 默认 UI 的依赖:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
这时候,UI 的风格是 Swagger 的默认风格。我们可以导入不同的依赖来更换不同的主题。
Bootstrap 风格:
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
LayerUI 风格:
<!-- https://mvnrepository.com/artifact/com.github.caspar-chen/swagger-ui-layer -->
<dependency>
<groupId>com.github.caspar-chen</groupId>
<artifactId>swagger-ui-layer</artifactId>
<version>1.1.3</version>
</dependency>
我们只需要在 Maven仓库 中搜索关键词 swagger-ui,就可以找到很多的主题了。😎
2. 任务
2.1 异步任务
所谓异步任务,就是异步处理的任务。比如,我们在网站上发邮件,后台就会执行发邮件的逻辑,但是发邮件这个操作需要一定的时间,不是立刻就发送成功的,在这期间,如果不使用异步处理,就会造成前台无响应,直到邮件发送成功,前台才有响应,这是十分不友好的。因此,我们对于一些任务需要进行异步处理,即:采用多线程的方式来处理这些任务。
异步任务操作
创建一个 SpringBoot 项目,在项目中创建一个名为 service 的包。
在 service 包下创建一个名为 AsyncService 的类。
在 AsyncService 中编写一个方法,给线程进行休眠,模拟业务响应:
@Service
public class AsyncService {
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("数据处理完毕");
}
}
然后再创建一个 controller 包,在包下创建一个名为 AsyncController 的控制类:
@RestController
public class AsyncController {
@Autowired
AsyncService asyncService;
@RequestMapping("/hello")
public String hello(){
asyncService.hello();
return "OK";
}
}
启动程序,访问 http://localhost:8080/hello。这时,网页会卡顿三秒(转圈三秒),然后跳转界面并显示 「OK」 在界面上。
上面模拟的是同步等待的情况,在实际业务中,我们想让用户直接得到信息,可以在后台使用多线程的方法,但是每次自己手写显得过于麻烦,因此,SpringBoot 给了我们一个封装的注解 @Async。
给业务处理方法加上注解 @Async:
@Service
public class AsyncService {
//告诉Spring这是一个异步方法
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("数据处理完毕");
}
}
还需要在主启动类上加上一个异步的注解 @EnableAsync:
@SpringBootApplication
//开启异步注解
@EnableAsync
public class Springboot08TestApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot08TestApplication.class, args);
}
}
最后,我们再运行项目,访问 http://localhost:8080/hello。这个时候,界面不会出现卡顿,而是直接跳转至消息界面,所对应的,后台正在进行处理:三秒后,控制台打印 数据处理完毕。
2.2 邮件发送
引入依赖:
<!--javax.mail:配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
从依赖名我们可以看出,这是 SpringBoot 官方的启动器。我们可以先点进去看一下,在这个依赖中可以看到:
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>1.6.4</version>
<scope>compile</scope>
</dependency>
这个依赖就是用来完成邮件发送的。
除此之外,我们可以全局搜索 MailAutoConfiguration,看看 SpringBoot 有没有编写一个自动配置类。果不其然,我们发现了一个名为 MailSenderAutoConfiguration 的自动配置类。在这个类中,我们并没有发现注册的 bean,我们可以看看它导入的类中有没有。点击导入的第一个类:

进入类后:
![]()
然后,我们可以看一下配置文件 MailProperties:
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private String host;
private Integer port;
private String username;
private String password;
private String protocol = "smtp";
private Charset defaultEncoding = DEFAULT_CHARSET;
private Map<String, String> properties = new HashMap<>();
private String jndiName;
// ...
}
通过阅读代码,我们得知:我们可以在主配置文件中进行关于邮件发送的配置,然后 SpringBoot 就会自动进行装配。
我们在此使用 QQ 邮箱做示范。为了不让我们的密码直接暴露在代码前,我们需要开启 POP3 和 SMTP 服务以获取授权码,进入 QQ 邮箱后,设置 — 账户 — 开启服务:

然后,我们在主配置文件中进行配置:
spring.mail.username=cy.mofan@qq.com
spring.mail.password=开启服务后得到的授权码
spring.mail.host=smtp.qq.com
#开启加密验证
spring.mail.properties.mail.smtp.ssl.enable=true
最后,我们可以在测试类中编写测试代码:
@SpringBootTest
class Springboot08TestApplicationTests {
@Autowired
JavaMailSender mailSender;
@Test
void contextLoads() {
//一封简单的邮件
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 标题
simpleMailMessage.setSubject("Yang,你好!");
// 内容
simpleMailMessage.setText("hello boy");
// 发送给谁
simpleMailMessage.setTo("cy.mofan@qq.com");
// 发送者
simpleMailMessage.setFrom("cy.mofan@qq.com");
mailSender.send(simpleMailMessage);
}
@Test
void contextLoads2() throws MessagingException {
//一封复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装~
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
helper.setSubject("Yang");
helper.setText("<p style='color:red'>你在干嘛?</p>",true);
//附件 发送一张图片 第一个参数表示文件名
helper.addAttachment("1.png",new File("C:\\Users\\Administrator\\Desktop\\1.png"));
helper.setTo("cy.mofan@qq.com");
helper.setFrom("cy.mofan@qq.com");
mailSender.send(mimeMessage);
}
/**
* 邮件发送方法
* @param html 支持多文件上传
* @param subject 发送邮件标题
* @param text 邮件内容
* @param htmlTag 邮件内容内部是否开启html解析
* @param fileName 附件名
* @param filePath 附件地址
* @param recipient 收件人
* @param sender 发件人
* @throws MessagingException
* @Author mofan
*/
public void sendMail(Boolean html, String subject, String text, Boolean htmlTag,
String fileName, String filePath,
String recipient, String sender) throws MessagingException {
//一封复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装~
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,html);
helper.setSubject(subject);
helper.setText(text,htmlTag);
//附件
helper.addAttachment(fileName,new File(fileName));
helper.setTo(recipient);
helper.setFrom(sender);
mailSender.send(mimeMessage);
}
}
因为要测试发送邮件,所以 得联网 才能测试成功。
2.3 定时任务
所谓定时任务,就是在规定的时间上自动执行某个操作。比如:每天凌晨的时候,输出并分析前一天的日志信息,这就需要定时任务来完成。SpringBoot 为我们异步执行任务调度提供了两个接口:
- TaskExecutor 接口 -----> 任务调度者
- TaskScheduler 接口 -----> 任务执行程序
同时,还提供了两个注解:
@EnableScheduling(表示开启定时功能的注解,位置在主启动类上)@Scheduled(表示任务什么时候执行)
在 @Scheduled 中,属性需要使用 Cron 表达式,来表示任务什么时候执行。
Cron 表达式:Cron表达式在线工具
定时任务的使用
首先在 service 包下创建一个类 ScheduledService ,在其中编写一个方法,这个方法需要定时执行:
@Service
public class ScheduledService {
//在特定的时间执行这个方法
//cron表达式
//秒 分 时 日 月 周几
/**
30 15 10 * * ? 每天10点15分30秒执行一次
*/
@Scheduled(cron = "30 15 10 * * ?")
public void hello(){
System.out.println("hello,你被执行了......");
}
}
然后在主启动类上使用 @EnableScheduling 注解:
@SpringBootApplication
//开启定时功能注解
@EnableScheduling
public class Springboot08TestApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot08TestApplication.class, args);
}
}
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。
在这个文件中我们可以看到以下信息:
tickTime=2000
initLimit=10
syncLimit=5
# 临时数据存储的目录
dataDir=/tmp/zookeeper
# ZK默认端口号
clientPort=2181
完成简单的修改后,我们在启动服务。双击运行 zkServer.cmd:

表示启动成功!
然后我们运行 zkCli.cmd,启动客户端。
注意:一定要先启动服务,并且保持服务的黑窗口一直运行!
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
启动客户端后可能会出现上述情况,我们点击回车即可。
启动后,在客户端查看一下所有节点:
ls /
# 只显示zookeeper一个节点
我们还可以自己创建一个节点:
# 创建一个名为mofan,值为yang的节点
create -e /mofan yang
获取创建节点的值:
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 文件:
server.port=7001
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root
spring.guest.password=guest
dubbo.registry.address=zookeeper://127.0.0.1:2181
如果你修改了 ZK 的地址端口,这里也需要修改!
然后我们需要在 项目目录下 打包 dubbo-admin,使用命令行,运行:
mvn clean package -Dmaven.test.skip=true
先启动 ZK 的服务!一定要先启动,因为 dubbo 依赖 ZK!
再执行 dubbo-admin\target 下的 dubbo-admin-xxx.jar:
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 表示服务提供者模块。
然后导入依赖:
<!--导入依赖:Dubbo ZK-->
<!-- dubbo-spring-boot-starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!--ZK Client-->
<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>
创建一个 service 包,在包下创建 TicketService 接口:
public interface TicketService {
public String getTicket();
}
再在包下创建接口的实现类 TicketServiceImpl:
package com.yang.service;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
/**
* @author 默烦
* @date 2020/6/13
*/
// ZK:服务注册与发现
@Service // 导入Dubbo的@Service注解,可以被扫描到,在项目一启动就主动注册到注册中心,暴露服务
@Component // 使用Dubbo尽量不要@Service,用于区分
public class TicketServiceImpl implements TicketService{
@Override
public String getTicket() {
return "hello,provider-server";
}
}
我为什么要将导入的包也复制进来? 注意此处的 @Service 注解是 Dubbo 的。
在配置文件中进行配置:
server.port=8001
# 服务应用名字
dubbo.application.name=provider-server
# 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# dubbo.registry.protocol=zookeeper dubbo 注册协议名
# dubbo.protocol.name 指定通信协议
# dubbo.protocol.port 指定通信端口
# 哪些服务被注册
dubbo.scan.base-packages=com.yang.service
如果不在配置文件中设置 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 类:

在这个类中编写代码:
package com.yang.service;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
/**
* @author 默烦
* @date 2020/6/13
*/
@Service // 放到容器中
public class UserService {
// 获取provider提供的票,要去注册中心拿到服务
@Reference // 引用服务,pom坐标或定义路径相同的接口名
TicketService ticketService;
public void buyTicket(){
String ticket = ticketService.getTicket();
System.out.println("在注册中心拿到了=>"+ticket);
}
}
我为什么要将导入的包也复制进来? 注意此处的 @Service 注解是 Spring 的。
在配置文件中进行配置:
server.port=8002
# 消费者去哪里获取服务并暴露自己的名字
dubbo.application.name=consumer-server
# 注册中心的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
编写测试类进行测试:
@SpringBootTest
class ConsumerServerApplicationTests {
@Autowired
UserService userService;
@Test
void contextLoads() {
userService.buyTicket();
}
}
然后,运行 consumer-server 模块,在控制台中查看输出:

