在之前的文章中介绍了 Eureka 微服务实现方式,今天则聚焦于另一实现方式:Spring Cloud Alibaba。
其提供了核心组件 Nacos 贯穿于微服务整个声明周期,为不同服务间架起沟通的桥梁,下面就让我们共同了解如何在项目工程中集成使用。
一、服务部署
1. 应用下载
Nacos 作为独立的服务需单独部署,进入 Nacos 官网根据需要选择对应版本,直达:Nacos。
2. 持久化
下载完成后需要修改 Nacos 的相关配置,如服务模式与数据的持久化配置。
在 MySQL 中新建数据库 nacos ,然后解压上一步下载的文件并运行 /conf/mysql-schema.sql 脚本文件。
创建数据库之后修改解压目录下的 /conf/application.properties 文件,找到下图中配置取消注释并替换为你的数据库配置。
3. 服务部署
完成上述步骤之后即可启动 Nacos 服务,这里以 Linux 为例,进入解压后的 bin 目录,编辑 startup.sh 脚本将默认启动模式设置为单体模式。
将图中的 mode 修改为 standalone ,然后通过命令 sh startup.sh 启动服务。
启动服务后访问 ip:8848/nacos 即可看到下图内容。
二、服务注册
在上一篇文章中介绍了服务中心的创建与服务激活,在 Nacos 中其自身即服务注册中心,因此无需再自建注册中心直接使用 Nacos 即可,因此这里直接新建 nacos-client 工程模拟服务通过 Nacos 实现服务注册。
之前的文章中提到过 Spring Cloud 的版本与 Spring Boot 的版本相互关联,这里引入的为 Finchley 版本因此 Spring Boot 版本需为 2.0.x 。
1. 项目配置
在工程的 POM 文件中导入 alibaba 与 Finchley 依赖,同时引入 nacos-discovery 用于服务注册。
文中的涉及的工程中 Spring Boot 版本均为 2.0.x 版本且都导入 alibaba 与 Finchley 依赖,后续将省略不再具体列出。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<artifactId>nacos-client</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
完成依赖引入后在工程的 yml 文件中添加下述内容,其中 server-addr 为 Nacos 服务地址。
server:
port: 8091
spring:
application:
name: nacos-client
cloud:
nacos:
discovery:
server-addr: 192.168.0.20:8848
2. 注册激活
为了后续更方便测试基于 RESTFUL 添加一个测试接口。
@RestController
@RequestMapping("/api/client")
public class ClientController {
@GetMapping("/hello")
public String hello() {
return "Hello world!";
}
}
在工程启动类上添加 @EnableDiscoveryClient 注解激活服务注册。
@EnableDiscoveryClient
@SpringBootApplication
public class NacosClientApplication {
public static void main(String[] args) {
SpringApplication.run(NacosClientApplication.class, args);
}
}
完成后启动项目访问 Nacos 服务页面即可看到 nacos-client 已成功实现服务注册。
三、服务通讯
在 Eureka 微服务文章中讲解了如何实现服务之间的通讯消费,在 Alibaba 中由 Nacos 注册服务之后同理可通过 LoadBalancerClient 或 Feign 实现服务消费,这里以之前 Feign 消费为例。
1. 项目配置
新建 nacos-feign 工程,在模块依赖中添加 openfeign 依赖,略去 Spring parent 与 Dalston 。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Feign service -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
依赖配置后在工程的 YML 配置文件设置 Nacos 服务地址进行服务注册。
server:
port: 8092
spring:
application:
name: nacos-feign
cloud:
nacos:
discovery:
server-addr: 192.168.0.20:8848
2. 服务请求
在服务调用中也及其简单,通过 @FeignClient 注解便可基于节点名称调用同一微服务环境下服务资源,而需接口请求定义则遵循 RESTFUL 风格进行定义。
如 nacos-client 服务节点存在 Get 接口 /api/get,则通过下述定义方式便可实现资源请求,略去了繁杂的 HTTP 资源对象管理。
@FeignClient("nacos-client")
public interface ClientFeign {
@GetMapping("/api/client/hello")
String hello();
}
完成 ClientFeign 配置之后新建测试接口,通过 @Autowired 注入 ClientFeign 实例并实现服务调用。
@RestController
@RequestMapping("/api/feign")
public class FeignController {
@Autowired
private ClientFeign clientFeign;
@GetMapping("/hello")
public String hello() {
return clientFeign.hello();
}
}
完成后在项目启动类添加 @EnableFeignClients 用于指定 ClientFeign 等接口包路径用于启动扫描。
@EnableFeignClients(basePackages = "xyz.ibudai.feign")
@EnableDiscoveryClient
@SpringBootApplication
public class NacosFeignApplication {
public static void main(String[] args) {
SpringApplication.run(NacosFeignApplication.class, args);
}
}
同时启动 nacos-client 与 nacos-feign 工程,访问 Nacos 服务列表可以看见两个服务都已注册成功。
通过 API 工具请求 nacos-feign 服务的 /demo 接口,可以看到成功实现服务 nacos-client 中的接口请求并返回结果。
3. 网络请求
在刚才的示例中基于 openfeign 实现同个微服务下的节点通讯,如若在不同服务网络中呢?
对于此类场景,openfeign 同样提供了 url 通讯模式,以实现资源访问且同样无需手动管理 HTTP 对象。
在 @FeignClient 注解中同个 url 属性即可替代上述的服务节点,如下示例中即定义声明了服务资源 localhost:8091/api/client/hello。
通过此方式即便服务节点并不是注册到同一 Nacos 实例环境下,仍然可实现资源访问。
@FeignClient(url = "localhost:8091")
public interface ClientFeign {
@GetMapping("/api/client/hello")
String hello();
}
4. 请求配置
在 HTTP 资源服务请求中,常涉及服务认证等场景,在 openfeign 中也同样提供了配置入口。
在 @FeignClient 注解中提供了 configuration 属性用于实现自定义拦截器,在拦截器中可则可手动实现认证服务等逻辑。
在请求拦截器中则通过 RequestTemplate 获取目标接口信息,实现一系列相应业务。
@Configuration
public class FeignAuthConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 获取服务名
String feignClientName = template.feignTarget().name();
// 获取请求路径,如 /user/info?id=1
String url = template.url();
// 获取 HTTP 方法
Request.HttpMethod method = template.method();
// 获取 Query 参数
Map<String, Collection<String>> queries = template.queries();
// 获取请求头
Map<String, Collection<String>> headers = template.headers();
// 获取请求体(POST请求)
String body = template.body() != null
? new String(template.body(), StandardCharsets.UTF_8)
: null;
// 添加请求头
template.header("Authorization", "Bearer STATIC_TOKEN_ABC123");
}
}
完成拦截器定义后便可通过 configuration 属性进行配置,每次请求时都将触发拦截器中所定义逻辑。
@FeignClient(name = "nacos-client", configuration = FeignAuthConfig.class)
public interface ClientFeign {
@GetMapping("/api/client/hello")
String hello();
}
四、配置中心
Nacos 除了实现服务注册中心之外同时提供了另一重要作用即分布式配置中心,即通过 Nacos 实现各个服务模块的 YML 配置文件的统一托管。
1. 项目配置
新建 nacos-config 工程,在工程中添加 nacos-config 依赖,略去 Spring parent 与 alibaba 等导入依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
在工程 resources 目录下新建 bootstrap.yml 配置文件添加如下内容,注意不是使用默认的 application.yml 文件,二者区别在于前者的加载优先级更高。
项目配置中的 group 与 discovery 和 config 中的 namespace 两项可以略去不填,默认值分别为 DEFAULT_GROUP 和 public。
其中 discovery 与 config 两组配置分别对应 Nacos 配置页面的 服务列表 和 配置列表。
server:
port: 8093
spring:
application:
name: nacos-config
cloud:
nacos:
# Default: "DEFAULT_GROUP"
group: DEFAULT_GROUP
discovery:
# Default: public
namespace: public
server-addr: 192.168.0.20:8848
config:
# namespace, use id not name
namespace: ${spring.cloud.nacos.discovery.namespace}
username: nacos
password: nacos
# suffix, default: properties
file-extension: yml
server-addr: ${spring.cloud.nacos.discovery.server-addr}
完成上述定义后在 Nacos 页面访问 配置管理 -> 配置列表,在左上角选择 创建配置。
其中 Data Id 与项目配置文件中的 spring.application.name 值保持一致,GROUP 选用默认值即可,文件格式选择 yaml,具体配置参考下图。
2. 公共配置
Nacos 同时支持公共服务的配置,通过 shared-dataids 与 refreshable-dataids 属性配置,前端指定文件名称,后者配置时候开启动态刷新(即配置变更无需重复服务)。
通过公共配置服务即可实现基础配置的复用,如系统的数据库连接配置与日志监控配置等信息,无需为每个服务模块编写重复配置,各个模块可选择是否导入即可。
如下示例中我们新建了公共配置 micro-common.yml 配置并添加了一个测试属性 info.common。
在上述的 nacos-config 工程中 bootstrap.yml 文件的基础上添加下述内容读取公共配置,当 shared-dataids 中指定了多个配置时,定义越靠后的配置其文件中相同属性的优先级更高。
如定义了 shared-dataids: first.yml, second.yml 两个公共配置,当 first.yml 与 second.yml 中存在相同配置时最终将会应用 second.yml 中的配置。
spring:
cloud:
nacos:
config:
# Newer load config will override old
shared-dataids: micro-common.yml
# Auto refresh config
refreshable-dataids: micro-common.yml
3. 命名空间
在 Nacos 中通过命名空间 namespace 可实现多个项目的注册管理,不同 namespace 之间相互独立互不影响。
因此即便存在多个微服务项目通过 namespace 也以通过同一个 nacos 服务实现统一管理, namespace 创建如下,注意创建的 namespace 在工程 bootstrap.yml 文件配置时填写为 命名空间ID 而非 命名空间名 。
创建命名空间之后访问 配置列表 或 服务列表 即可看见新建命名空间。
4. 服务测试
新建服务测试接口通过 @Value 注解读取 Nacos 中的配置信息。
其中 @Value 与 @ConfigurationProperties 注解所配置的将会实时监听 Nacos 变化并生效,由 spring.cloud.nacos.config.refresh-enabled 配置控制且默认为 true。
完成后启动项目通过 API 工具请求 /name 测试接口可以看到成功读取 Nacos 中配置的信息。
@RestController
@RequestMapping("/api/config")
public class ConfigController {
@Value("${info.name}")
private String infoName;
@Value("${common.name}")
private String commonName;
@GetMapping("/name")
public String name() {
return infoName + " - " + commonName;
}
}
Talk is cheap, show me you code。
文中示例工程已上传 GitHub ,仓库直达。
参考链接:Spring Cloud 从入门到精通。