Spring Boot + Spring Cloud + Feign + Nacos 搭建微服务
搭建 Nacos 注册中心
Nacos 官网, 下载最新 zip 包, Nacos 快速开始
在服务器上解压缩, 进入 nacos/bin
目录
- 执行
./startup.sh -m standalone
启动服务 - 执行
./shutdown.sh
关闭服务
访问路径 http://localhost:8848, 初始账号/密码: nacos/nacos
创建项目
新建 Maven 项目
通过 IntelliJ IDEA 新建一个 Maven 演示项目 ut-demo-server
, 其中有两个模块
ut-demo-service
RPC 服务实现ut-demo-task
RPC 远程调用
创建完毕以后, 父项目的 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.ut</groupId>
<artifactId>ut-demo-server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>ut-demo-service</module>
<module>ut-demo-task</module>
</modules>
</project>
依赖版本控制
这里核心要控制如下三个的版本号:
spring-boot-dependencies
:2.5.3
spring-cloud-dependencies
:2020.0.3
spring-cloud-alibaba-dependencies
:2021.1
以上版本是截止 2021-08-19 最新的版本, Spring Boot 与 Spring Cloud 的版本依赖参考这里 https://start.spring.io/actuator/info
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.ut</groupId>
<artifactId>ut-demo-server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>ut-demo-service</module>
<module>ut-demo-task</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
服务的实现
在 ut-demo-service
中添加如下依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
新建 com.example.ut.Application
主入口文件, 代码如下
@RestController
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public String hello(@RequestParam(value = "name", required = false, defaultValue = "world") String name) {
return String.format("Hello, %s!", name);
}
}
执行 main 函数, 即可通过 http://localhost:8080/hello 访问.
此时, 我们也可以从控制台看见报错, serviceName is blank
, 服务也没有注册到 nacos 上, 这是因为我们缺少配置文件
spring:
application:
name: demo-service-dev
cloud:
nacos:
discovery:
server-addr: xxx.xxx.xxx.xxx:8848
再次启动服务, 没有报错, 且在 nacos 上可以看到服务注册的信息
最后, 为了生成可执行 jar 包, 增加如下插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
远程调用
在 ut-demo-task
中添加如下依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
新建接口定义文件 com.example.ut.UtDemoService
, 代码如下
@FeignClient("demo-service-dev")
public interface UtDemoService {
@GetMapping("/hello")
String hello(@RequestParam(value = "name", required = false, defaultValue = "world") String name);
}
新建 com.example.ut.Job
主入口文件, 代码如下
@EnableFeignClients
@SpringBootApplication
public class Job implements CommandLineRunner {
@Resource
private UtDemoService utDemoService;
@Override
public void run(String... args) throws Exception {
System.out.println(utDemoService.hello(null));
System.out.println(utDemoService.hello("henry"));
}
public static void main(String[] args) {
new SpringApplicationBuilder().sources(Job.class).web(WebApplicationType.NONE).run(args).close();
}
}
最后增加 application.yml
配置文件, 与 ut-demo-service
类似, 将 application name 改为 ut-demo-task
.
执行 Job 可以看到如下输出
Hello, world!
Hello, henry!
拆分接口模块
前面我们已经基于 Spring Cloud 实现了微服务, 以及远程调用, 但我们也看到两个模块有重复的接口定义代码, 而且服务提供方与调用方往往不是同一个人, 我们是否能将接口定义部分抽离, 由服务开发的同学提供.
新增接口模块
创建 ut-demo-api
子模块, 将 UtDemoService
移动过去, ut-demo-task
依赖 ut-demo-api
即可.
执行 Job 可以通过测试.
接口与实现保持一致
让 ut-demo-service
依赖 ut-demo-api
, 新建实现类 UtDemoController
, 内容如下
@RestController
public class UtDemoController implements UtDemoService {
@Override
public String hello(String name) {
return String.format("Hello, %s!", name);
}
}
原 Application
中的实现就可以删除了
AutoConfiguration
修改 UtDemoService 的包路径为 org.enthusa
, 再次运行 Job 提示如下错误
A component required a bean of type 'org.enthusa.UtDemoService' that could not be found.
修改 @EnableFeignClients
注解, 增加 basePackages = {"org.enthusa"}
参数即可.
然而, 更加优雅的方式是, 新增 org.enthusa.UtDemoServiceAutoConfiguration
配置类, 内容如下
@Configuration
@EnableFeignClients
public class UtDemoServiceAutoConfiguration {
}
新增资源文件 META-INF/spring.factories
, 内容如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.enthusa.UtDemoServiceAutoConfiguration
这样运行不报错了, 不过由于 service 模块也依赖了 api 模块, 也会自动装配这个 bean, 不合理. 那么我们增加一个自动装配触发的条件
@ConditionalOnProperty(prefix = "rpc.ut-demo-service", name = "service-name")
在 task 模块的配置中增加如下信息, 即可通过
rpc:
ut-demo-service:
service-name: demo-service-dev