什么是服务发现
在微服务架构中,整个系统会按职责能力划分为多个服务,通过服务之间协作来实现业务目标。这样在我们的代码
中免不了要进行服务间的远程调用,服务的消费方要调用服务的生产方,为了完成一次请求,消费方需要知道服务
生产方的网络位置(IP地址和端口号)。
我们的代码可以通过读取配置文件的方式读取服务生产方网络位置,如下:
看上去很完美,但是,仔细考虑以下,此方案对于微服务应用而言行不通。
首先,微服务可能是部署在云环境的,服务实例的网络位置或许是动态分配的。另外,每一个服务一般会有多个实
例来做负载均衡,由于宕机或升级,服务实例网络地址会经常动态改变。再者,每一个服务也可能应对临时访问压
力增加新的服务节点。正如下图所示:
基于以上的问题,服务之间如何相互感知?服务如何管理?这就是服务发现的问题了。如下图:
上图中服务实例本身并不记录服务生产方的网络地址,所有服务实例内部都会包含服务发现客户端。
(1)在每个服务启动时会向服务发现中心上报自己的网络位置。这样,在服务发现中心内部会形成一个服务注册表,服务注册表是服务发现的核心部分,是包含所有服务实例的网络地址的数据库。
(2)服务发现客户端会定期从服务发现中心同步服务注册表 ,并缓存在客户端。
(3)当需要对某服务进行请求时,服务实例通过该注册表,定位目标服务网络地址。若目标服务存在多个网络地
址,则使用负载均衡算法从多个服务实例中选择出一个,然后发出请求。
总结一下,在微服务环境中,由于服务运行实例的网络地址是不断动态变化的,服务实例数量的动态变化 ,因此无
法使用固定的配置文件来记录服务提供方的网络地址,必须使用动态的服务发现机制用于实现微服务间的相互感
知。各服务实例会上报自己的网络地址,这样服务中心就形成了一个完整的服务注册表,各服务实例会通过服务发
现中心来获取访问目标服务的网络地址,从而实现服务发现的机制。
主流服务发现与配置中心对比
目前市面上用的比较多的服务发现中心有:Nacos、Eureka、Consul和Zookeeper。
对比项目 | Nacos | Eureka | Consul | Zookeeper |
一致性协议 | 支持AP和CP模型 | AP模型 | CP模型 | CP模型 |
健康检查 | TCP/HTTP/MYSQL/Client Beat | Client Beat | TCP/HTTP/gRPC/Cmd | Keep Alive |
负载均衡策略 | 权重/metadata/Selector | Ribbon | Fabio | - |
雪崩保护 | 有 | 有 | 无 | 无 |
自动注销实例 | 支持 | 支持 | 不支持 | 支持 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 |
跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 |
SpringCloud集成 | 支持 | 支持 | 支持 | 不支持 |
Dubbo集成 | 支持 | 不支持 | 不支持 | 支持 |
k8s集成 | 支持 | 不支持 | 支持 | 不支持 |
从上面对比可以了解到,Nacos作为服务发现中心,具备更多的功能支持项,且从长远来看Nacos在以后的版本会
支持SpringCLoud+Kubernetes的组合,填补 2 者的鸿沟,在两套体系下可以采用同一套服务发现和配置管理的解
决方案,这将大大的简化使用和维护的成本。另外,Nacos 计划实现 Service Mesh,也是未来微服务发展的趋势
Nacos的服务发现
Nacos主要提供以下四大功能:
1. 服务发现与服务健康检查
- Nacos使服务更容易注册,并通过DNS或HTTP接口发现其他服务,Nacos还提供服务的实时健康检查,以防止向不健康的主机或服务实例发送请求。
2. 动态配置管理
- 动态配置服务允许您在所有环境中以集中和动态的方式管理所有服务的配置。Nacos消除了在更新配置时重新部署应用程序,这使配置的更改更加高效和灵活。
3 . 动态DNS服务
- Nacos提供基于DNS 协议的服务发现能力,旨在支持异构语言的服务发现,支持将注册在Nacos上的服务以域名的方式暴露端点,让三方应用方便的查阅及发现。
4. 服务和元数据管理
- Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略。
这里1、3、4说明了服务发现的功能特性。
父项目
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
生产者:
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
配置文件
server:
port: ${port:56010} #启动端口
spring:
application:
name: quickstart-provider
cloud:
nacos:
discovery:
server-addr: xxxxx:8848
logging:
level:
root: info
org.springframework: info
web层
@RestController public class ProviderController { private static final Logger LOG = LoggerFactory.getLogger(ProviderController.class); @GetMapping("/service") public String service(){ LOG.info("provider invoke"); return "provider invoke"; } }
启动类
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class NacosProviderApp { public static void main(String[] args) { SpringApplication.run(NacosProviderApp.class,args); } }
消费
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
Feign的调用
@FeignClient(value = "quickstart-provider") public interface ProviderClient { @GetMapping("/service") public String service(); }
配置类:
server: port: 56020 #启动端口 命令行注入 spring: application: name: quickstart-consumer cloud: nacos: discovery: server-addr: xxxxxx:8848
web层
@RestController public class ConsumerController { //动态代理对象,内部远程调用服务生产者 @Autowired ProviderClient providerClient; @GetMapping("/service") public String service(){ //远程调用 String providerResult = providerClient.service(); return "consumer invoke" + "|" + providerResult; } }
然后开启服务
查看注册中心:
进行调用,发现由于fegin在,所以会有轮询的效果