SpringCloud Netflix进入维护模式,意味着不再开发新的组件,这就出现了SpringCloud Alibaba这个替代品,SpringCloud Alibaba能实现以下功能,也可以参考SpringCloud Alibaba官网:
功能 | 具体说明 |
---|---|
服务限流与降级 | 默认支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics监控。 |
服务注册与发现 | 适配SpringCloud服务注册与发现标准,默认集成了Ribbon的支持。 |
分布式配置管理 | 支持分布式系统中的外部化配置,配置更改时自动刷新。 |
消息驱动能力 | 基于SpringCloud Stream为微服务应用构建消息驱动鞥能力。 |
阿里云对象存储 | 阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。 |
分布式任务调度 | 提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker上执行。 |
使用SpringCloud Alibaba需要在POM文件中引入其依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
“Nacos”,前四个字母分别问Naming和Configuration的前两个字母,最后的s为Service,它是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台,说白了就是注册中心+配置中心的组合,等价于SpringCloud之前的Eureka+Config+Bus,所以Nacos可以替代Eureka做服务中心,可以替代Config做配置中心。
在Nacos官网下载地址中下载Nacos,在安装Nacos前需要本地Java8和Maven的环境已经准备好,然后下载其压缩包:
解压该压缩文件后直接运行在bin目录下的startup.cmd启动Nacos:
然后访问 http://localhost:8848/nacos/ 进入Nacos,其默认的账号密码都是nacos,登录成功后即可看到如下界面:
在之前学习服务注册中心的时候讲过CAP理论,即
C | A | P |
---|---|---|
Consistency | Available | Partition tolerance |
强一致性 | 可用性 | 分区容错性 |
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性、可用性、分区容错性。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。在分布式架构中,P永远要求被保证,所以当前的分布式架构只有AP和CP两种。Nacos属于AP模型,再次对比这些服务注册中心:
服务注册与发现框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP | 支持 | 低(2.x版本历史问题) |
Zookeeper | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP(事实上也可以支持CP) | 支持 | 高 |
据说Nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验。。。
新建Module:cloudalibaba-provider-payment9002作为服务提供方微服务。当然为了实现Nacos的负载均衡,使nacos-payment-provider服务集群化,我们同事也仿照9002搭建一个9003微服务。
然后在工程的父POM中引入依赖1. Spring Cloud Alibaba简介中引入的依赖的前提下,在该模块的POM引入依赖Nacos服务注册发现的依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
然后添加其配置文件application.yml:
server:
port: 9002
spring:
application:
name: nacos-payment-provider # 微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*' # 监控端点全部打开
编写其主启动类并在主启动类上添加 @EnableDiscoveryClient
注解,使9002微服务能够被注册中心发现:
package cn.sher6j.springcloudalibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author sher6j
* @create 2020-05-26-9:56
*/
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9002 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class);
}
}
然后编写一个简单的业务类:
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id) {
return "Nacos服务注册,端口:" + serverPort + ";id:" + id;
}
}
启动9002、9003微服务模块,在Nacos的服务列表中我们可以看到这两个微服务已经入驻服务注册中心:
点开nacos-payment-provider服务的详情页面,可以看到服务的实例详情:
新建Module:cloudalibaba-consumer-nacos-order83作为服务消费方微服务,在该模块的POM中同样引入令Nacos服务注册中心发现自己的依赖,配置其配置文件:
server:
port: 83
spring:
application:
name: nacos-order-cosumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册成功进Nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
然后编写服务消费方的主启动类,Nacos本身就具有负载均衡功能,因为引入Nacos依赖的同时,Nacos内部集成了Ribbon,如图所示:
而我们在学习Ribbon时知道,用了Ribbon就需要使用 RestTemplate
,所有我们编写配置类,向Spring容器中注入 RestTemplate
:
package cn.sher6j.springcloudalibaba.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author sher6j
* @create 2020-05-26-10:40
*/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
注意注入 RestTemplate
时一定要添加 @LoadBalanced
注解,否则不会开启负载均衡,在服务提供方集群的情况下,由于没有开启负载均衡,消费方会无法选择具体调用哪个微服务实例,也就是服务提供方实例有很多个,而消费方服务不知道要调用哪个具体的服务提供方实例,不加该注解会产生 UnknownHostException
的异常:
然后编写其业务类:
package cn.sher6j.springcloudalibaba.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author sher6j
* @create 2020-05-26-10:42
*/
@RestController
@Slf4j
public class OrderNacosController {
@Autowired
private RestTemplate restTemplate;
//直接读取配置文件中的值,减少代码冗余
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping("/consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") Long id) {
return restTemplate.getForObject(serverURL + "/payment/nacos/" + id, String.class);
}
}
启动83服务消费方,在Nacos服务注册中心中我们可以看到入驻了两个服务提供方实例和一个服务消费方实例:
多次访问 http://localhost:83/consumer/payment/nacos/1 (id值随意,紧作为测试用1代替),我们发现服务消费方微服务83可以完成对服务提供方微服务9002/9003的轮询负载均衡调用,也就是Nacos内部就整合了Ribbon,实现了负载均衡,默认负载均衡算法采用轮询:
事实上,Nacos可以AP模式和CP模式中进行切换,也就是说Nacos不仅仅支持AP(可用性和分区容错性),它同样支持CP(一致性和分区容错性)。当采取入驻到Nacos服务注册中心的微服务对自己的健康状态进行上报时,也就是对入驻到注册中心的微服务进行非持久化的保存,一旦客户端上报不健康信息,就将不健康的实例摘除掉,这类似于Eureka(当然Eureka可以开启自我保护模式);当采取由Nacos服务注册中心自己探测入驻到中心的微服务是否健康时,也就是对入驻到注册中心的微服务进行持久化保存,即使服务注册中心发现微服务已经不健康了,也不会删除到微服务,这类似于Consul。如下图(CoreDNS也是一种服务注册中心):
Nacos与其他服务注册中心特性的对比:
Nacos | Eureka | Consul | CoreDNS | Zookeeper | |
---|---|---|---|---|---|
一致性协议 | CP/AP | AP | CP | / | CP |
健康检查 | TCP/HTTP/MySQL/Client Beat(客户端心跳) | Clent Beat | TCP/HTTP/gRPC/cmd | / | Client Beat |
负载均衡 | 权重/DSL/元数据/CMDB | Ribbon | Fabio | RR | / |
雪崩保护 | 支持 | 支持 | 不支持 | 不支持 | 不支持 |
自动注销实例 | 支持 | 支持 | 不支持 | 不支持 | 支持 |
访问协议 | HTTP/DNS/UDP | HTTP | HTTP/DNS | DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
跨注册中心 | 支持 | 不支持 | 支持 | 不支持 | 不支持 |
SpringCloud集成 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
Dubbo集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 |
K8s集成 | 支持 | 不支持 | 支持 | 支持 | 不支持 |
从理论知识中我们知道Nacos服务注册中心可以在AP和CP模式中进行切换,C是所有节点在同一时间看到的数据是一致的,而A是所有的请求都会受到响应(A可以近似理解为高可用)。
一般来说,如果不需要存储服务级别的信息且服务实例是通过Nacos-Client注册的,并且服务能保持心跳上报,那么就可以选择AP模式,当前主流的微服务框架如SpringCloud和Dubbo,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
而如果需要在服务级别编辑或存储配置信息,那么CP是必须的,K8s服务和DNS服务使用于CP模式,CP模式下则支持注册持久化实例,此时以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
如果需要让Nacos服务从AP模式切换到CP模式的话,只需要向服务注册中心发送POST请求即可:
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
在用SpringCloud Config结合SpringCloud Bus时,我们可以把配置信息托管到远端如GitHub上,而现在我们应用了Nacos后,可以直接在Nacos上托管配置信息。
新建Module:cloudalibaba-config-nacos-client3377作为服务配置中心微服务,在其POM文件中引入必要的依赖(如Nacos服务注册中心的依赖)为,引入配置中心的依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
Nacos同SpringCloud Config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置后,才能保证项目的正常启动,而我们知道SpringBoot配置文件的加载顺序是存在优先级的,bootstrap的优先级要高于application,所以我们创建两个配置文件 bootstrap.yml
(从配置中心拉取配置)和 application.yml
(写自己的配置),我们配置 bootstrap.yml
以使3377服务从Nacos上拉取配置信息:
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml # 指定yaml格式的配置,也就是说从Nacos上读yaml格式的配置文件
然后用 application.yml
定制自己的配置信息,将环境定义为开发环境:
spring:
profiles:
active: dev # 表示开发环境
编写其主启动类后,然后编写其业务类,在业务来上添加SpringCloud的原生注解**@RefreshScope
以使服务可以支持从Nacos配置中心动态刷新配置信息**,
package cn.sher6j.springcloudalibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author sher6j
* @create 2020-05-28-21:07
*/
@RestController
@RefreshScope //支持Nacos的动态刷新功能
public class ConfigClientController {
@Value("{config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
在上面的业务层中我们从配置中心读取了配置信息
而我们在Nacos配置中心中添加配置信息,在Nacos配置中心中添加配置文件要遵循一定的匹配规则——Nacos中的 dataId
的组成格式及与SpringBoot配置文件中的匹配规则,更详细的信息可以参考Nacos官方文档,在Nacos SpringCloud中,dataId
的完整格式为:
${prefix}-${spring.profile.active}.${file-extension}
prefix
默认为 spring.application.name
(服务名)的值,也可以通过配置项 spring.cloud.nacos.config.prefix
来配置。spring.profile.active
即为当前环境对应的 profile(上面我们在 application.yml
中配置的属性),详情可以参考 Spring Boot文档。 注意:当 spring.profile.active
为空时,对应的连接符 -
也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
, 建议不要使spring.profile.active
为空。file-exetension
为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension
来配置。目前只支持 properties
和 yaml
类型。由于prefix
默认为 spring.application.name
(服务名)的值,所以 dataId
的完整格式可以替换为:
${spring.application.name}-${spring.profile.active}.${file-extension}
结合我们在 boostrap.yml
配置文件和 application.yml
配置文件的配置,我们可以得到在当前实例中的 dataId
应该为:
nacos-config-client-dev.yaml
根据上述公式得到 dataId
应为的值后,我们就可以在Nacos配置中心的配置列表中新建配置文件:
在新建配置文件时,Data ID
中填入我们得到的 dataId
,组名先选择默认的即可,配置格式选择我们在 file-extension
中设置的YAML格式,然后编写配置文件,编写后点击发布即可:
注意,在Nacos中的 dataId
中的后缀名必须用 yaml
而不能用 yml
。添加配置文件后再点开Nacos配置中心的配置列表,就能发现已经存在刚才创建的配置文件:
然后我们启动3377微服务,访问 http://localhost:3377/config/info 查看能否访问都Nacos配置中心的配置信息:
Nacos配置中心直接就支持动态刷新,再更给了Nacos配置中心的配置信息后,再通过3377微服务访问,就可以得到更新后的配置信息。目前Nacos已经实现了Eureka + Config + Bus的功能,但是Nacos之所以如此优秀是因为这些框架有的功能它有,而这些框架没有的功能它还有!下面看Nacos的一些高级功能。
在多环境多项目管理时,也就是实际开发中,通常一个系统会准备不同的开发环境,如dev开发环境、test测试环境、prod生产环境,如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境等待,那对这些微服务配置该如何管理呢?在Nacos配置中心中就可以进行分类配置,一个配置文件的具体所属由 namespace
+ Group
+ dataId
所构成:
这种设计就类似于Java中的包名和类名,最外层的 namespace
是可以用于区分部署环境的,主要用来实现隔离,比如有三个环境:开发、测试、生产,那就可以创建三个 namespace
。Group
和 dataId
逻辑上区分两个目标对象,默认情况下 namespace
的值为 public
, Group
的值为 DEFAULT_GROUP
。 下面对这三个属性进行详细展开。
dataId
方案指定 spring.profile.active
配置属性和配置文件的 dataId
来使不同环境下读取不同的配置,也就是说我们现在用默认的命名空间 namespace
(即 public
),默认的分组 Group
(即 DEFAULT_GROUP
),然后在Nacos配置中心中建立两个 dataId
分别为 dev
环境和 test
环境:
此时在同一命名空间同一组中就有了两个应用于开发、测试不同环境的配置文件:
然后我们通过修改微服务自己定制配置信息 application.yml
配置文件中的spring.profile.active
属性即可进行多环境下的配置文件的读取,配置什么就加载什么,在之前我们的3377微服务获取的是配置中心中 nacos-config-client-dev.yaml
配置文件的配置信息,现在我们将 application.yml
修改为:
spring:
profiles:
# active: dev # 表示开发环境
active: test # 表示开发环境
然后重启3377微服务,再次进行测试发现访问到的配置信息为测试环境的配置信息:
这就类似于在Java中的包名相同,但是类名不同。
Group
方案不仅通过 dataId
可以实现环境分区,应用 Group
也可以实现环境分区,首先我们在Nacos配置中心新建两个相同 dataId
,不同 Group
的配置文件,一个用于开发环境,一个用于测试环境:
由于这两个配置文件的 dataId
均为 nacos-config-client-info.yaml
,根据之前解释的 dataId
的命名规则,我们应该将3377微幅的自定义配置文件 application.yml
中的 spring.profile.active
属性更改为 info
:
spring:
profiles:
active: info
在确认了读取的配置文件的 dataId
后,我们在其 bootstrap.yml
配置文件中添加 spring.cloud.nacos.config.group
属性,设置为响应的组名即可,比如我们现在设置为测试环境组:
spring:
cloud:
nacos:
config:
group: TEST_GROUP
重启3377微服务,再次访问可以发现获取到的配置信息即为TEST_GROUP
组的配置信息:
namespace
方案根据namespace同样可以进行环境区分。