Nacos Discovery
是什么?
服务发现是微服务架构体系中最关键的组件之一。
如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于服务的动态扩缩容。Nacos Discovery 可以帮助用户将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。
除此之外,Nacos Discovery 也将服务实例自身的一些元数据信息,例如 host,port,健康检查URL,主页等-注册到 Nacos 。
关于 Nacos 的安装及启动请查看文章:Spring Cloud Alibaba:Nacos 安装及使用
本篇将详细介绍 Nacos Discovery
服务注册与发现的一些技术实践。
快速接入
添加依赖
启动好 Nacos 服务端。然后在项目中添加依赖。
1 2 3 4
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
|
一个完整的 pom.xml
的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?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.xingtuai.example</groupId> <artifactId>nacos-discovery-test</artifactId> <version>1.0-SNAPSHOT</version> <name>nacos-discovery-test</name>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version> <relativePath/> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
注:在本项目的源代码中,依赖统一使用 dependencies
模块管理,且其中 dependencyManagement
节点也在 dependencies
模块中配置,上例仅演示必要的配置项,在项目源代码中有些许区别。
启动 Provider 服务提供者
Nacos Discovery 服务注册与发现中,一般有两个角色,一个是 Provider 服务提供者,一个是 Consumer 服务消费者。他们都需要将自身注册到 Naocs 中,这一步叫服务注册。服务提供者向外提供服务,服务消费者通过各种方式调用服务提供者完成业务功能。且一个服务,既可以作为提供者,同时也可以作为消费者。
1. 配置 application.yml
要使用 Nacos ,需要在 application.yml
或者 bootstrap.yml
中配置一些基本参数。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| nacos: server-addr: 192.168.9.17:8848 username: nacos password: nacos namespace: 904174a3-d51f-43ed-a456-c4fd7386ecb3 spring: application: name: nacos-discovery-provider main: allow-bean-definition-overriding: true cloud: nacos: username: ${nacos.username} password: ${nacos.password} discovery: server-addr: ${nacos.server-addr} namespace: ${nacos.namespace} group: SPRING_CLOUD_EXAMPLE_GROUP metadata: version: 1.0 region: hangzhou
|
上述配置比较完善,如果需要简化配置,则如下所示:
1 2 3 4 5 6 7
| spring: application: name: nacos-discovery-provider cloud: nacos: discovery: server-addr: 192.168.9.17:8848
|
2. 开启服务发现
在项目启动类上添加注解 @EnableDiscoveryClient
即可。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
| import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class NacosDiscoveryProviderApplication { public static void main(String[] args) { SpringApplication.run(NacosDiscoveryProviderApplication.class, args); } }
|
3. 创建 REST 接口
服务提供者需要提供接口以供消费者进行调用,一般使用 RESTful 风格的 HTTP 接口。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping public class TestController {
@GetMapping("/echo/{message}") public String echo(@PathVariable String message) { return "Hello Nacos Discovery " + message; } }
|
4. 验证
启动 Provider 服务提供者后,打开 Nacos 控制台,可以看到服务已经注册上去了,因为我使用的是 dev
开发环境的命名空间,所以服务注册在 dev
的命名空间内。
启动 Consumer 服务消费者
Consumer 服务消费者的依赖的配置基本与 Provider 服务提供者相同,但是却要比它更复杂一点。因为在 Consumer 端需要去调用 Provider 端的 REST 服务。而调用方式也有很多种。
1. RestTemplate 方式
依赖、配置与 Provider 一致。
然后创建 RestTemplate
配置类。如下所示:
1 2 3 4 5 6 7 8 9 10 11
| import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate;
@Component public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
|
然后调用 Provider 服务提供者。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@Slf4j @RestController @RequestMapping public class BaseTestController {
@Resource private LoadBalancerClient loadBalancerClient;
@Resource private RestTemplate restTemplate;
@Value("${spring.application.name}") private String appName;
@GetMapping("/echo") public String echoAppName() { ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-discovery-provider"); String url = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), appName);
log.info("request url: {}", url); return restTemplate.getForObject(url, String.class); } }
|
启动项目后,打开浏览器请求:http://localhost:8080/echo
查看返回结果进行验证。
2. Feign 方式
首先需要添加 Feign
的依赖,如下所示:
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
添加完依赖以后,需要在项目启动类中添加 @EnableFeignClients
注解以开启功能。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class NacosDiscoveryConsumerApplication { public static void main(String[] args) { SpringApplication.run(NacosDiscoveryConsumerApplication.class, args); } }
|
然后需要创建一个接口类 FeignService
,在类中为 Provider 服务提供者提供的服务接口提供对应的访问方法。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("nacos-discovery-provider") public interface FeignService {
@GetMapping("/echo/{message}") public String echo(@PathVariable String message); }
|
在 Controller 中调用 FeignService
接口实现对 Provider 端的调用。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import com.xingtuai.cloud.nacos.discovery.service.FeignService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@Slf4j @RestController @RequestMapping("feign") public class FeignTestController {
@Resource private FeignService feignService;
@Value("${spring.application.name}") private String appName;
@GetMapping("/echo") public String echoAppName() { return feignService.echo(appName); } }
|
启动项目后,打开浏览器请求:http://localhost:8080/feign/echo
查看返回结果进行验证。
多环境隔离
Nacos Discovery
与 Nacos Config
的环境隔离是一样的,都是通过命名空间 namespace
进行区分。
具体请参考之前的文章:Spring Cloud Alibaba:Nacos Config 配置中心
在实际开发中,通过这种方式可以非常好的对各种环境进行隔离区分,避免服务管理的混乱。
结合 Sentinel 熔断
Sentinel
是阿里微服务生态中一个比较重要的组件,功能也比较多,在此仅简单介绍。后续会有专题对其进行详细的研究实践。
此处结合,主要是在 Feign 调用的时候,进行处理。在调用失败时,可以进行熔断。
首先需要添加依赖,如下所示:
1 2 3 4 5
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
|
在 application.yml
中进行配置,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| nacos: server-addr: 192.168.9.17:8848 username: nacos password: nacos namespace: 904174a3-d51f-43ed-a456-c4fd7386ecb3 sentinel: dashboard: 192.168.9.17:8883 spring: application: name: nacos-discovery-sentinel main: allow-bean-definition-overriding: true cloud: nacos: username: ${nacos.username} password: ${nacos.password} discovery: server-addr: ${nacos.server-addr} namespace: ${nacos.namespace} group: SPRING_CLOUD_EXAMPLE_GROUP sentinel: transport: dashboard: ${sentinel.dashboard}
feign: sentinel: enabled: true
|
创建一个 FeignService
接口方法失败时的回调类 FeignServiceFallback
,代码如下:
1 2 3 4 5 6
| public class FeignServiceFallback implements FeignService { @Override public String echo(String message) { return "echo fallback, please try again."; } }
|
当访问 echo(String message)
方法失败时,将会进入此回调,返回你需要的数据格式。
除此之外还需要创建一个配置类 FeignConfig
,代码如下:
1 2 3 4 5 6 7 8 9 10
| import com.xingtuai.cloud.nacos.discovery.service.fallback.FeignServiceFallback; import org.springframework.context.annotation.Bean;
public class FeignConfig {
@Bean public FeignServiceFallback feignServiceFallback() { return new FeignServiceFallback(); } }
|
然后需要改造一下 FeignService
接口,将 FeignServiceFallback
及 FeignConfig
设置在注解 @FeignClient
中,改造如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "nacos-discovery-provider", fallback = FeignServiceFallback.class, configuration = FeignConfig.class) public interface FeignService {
@GetMapping("/echo/{message}") public String echo(@PathVariable String message); }
|
正常访问时,接口将返回正常的数据,但是当接口出现异常,比如服务提供者下线了,访问失败的话。那么将会调用 FeignServiceFallback
中的 echo(String message)
方法并返回。这样就避免返回异常,而是返回一个可控的数据,可以用做服务熔断。
Sentinel 相关的知识与实践还有很多,后续会做专题分享,在此不再赘述。
项目源代码
GitHub - spring-cloud-example