Nacos
-
Nacos是什么?
Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 复制代码
-
Nacos能做什么?
1. 服务发现及管理 2. 动态配置服务 3. 动态DNS服务 复制代码
Nacos 官方地址: nacos.io/zh-cn/index…
注册中心
-
创建一个spring boot的微服务,使用阿里云镜像生成初始化代码工程:start.aliyun.com
-
导入内置的Nacos客户端所需要的依赖
-
微服务以web工程导入
# 应用名称
spring.application.name=sample-service
# 应用服务 WEB 访问端口
server.port=8080
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
复制代码
服务注册成功后,服务管理平台能看到对应的服务信息
-
心跳机制
由微服务内置的Nacos客户端每隔5秒向Nacos发起心跳包。心跳包包含了当前微服务的实例名称、IP、端口、集群名、权重等信息。 -
心跳包处理流程
Nacos Server 内置的逻辑是每过 20 秒对“实例 Map”中的所有“非健康”实例进行扫描,如发现“非健康”实例,随即从“实例 Map”中将该实例删除。
配置中心
-
创建配置
- Data ID:配置的唯一标识,格式固定为:{微服务id}-{环境名}.yml
- Group:指定配置文件的分组,这里设置默认分组 DEFAULT_GROUP 即可。
- 描述:说明配置文件的用途。
- 配置格式:指定“配置内容”的类型
- 配置内容:将工程的 application.yml 文件内容粘贴过来。
通过bootstrap.yml 引导文件,对 Nacos 配置中心地址进行设置。注意,bootstrap.yml 文件名是固定的,不要随意改变
spring:
application:
name: sample-service #微服务id
profiles:
active: dev #环境名
cloud:
nacos:
config: #Nacos配置中心配置
file-extension: yml #文件扩展名
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
logging: #开启debug日志
level:
root: debug
复制代码
-
验证Nacos配置生效
public class TestController {
@Value("${datasource.username}")
private String username;
@Value("${datasource.password}")
private String password;
@GetMapping("/test")
public String test(){
return "username:" + username + "<br/> password:" + password;
}
}
复制代码
- 配置中心数据持久化
Nacos在1.3.0之后提供了一个新的存储模式,那就是使用raft协议保证数据一致性,使用apache derby进行内嵌的数据存储。提供这种方式的目的是减少用户维护mysql数据库集群的成本,并且简化了集群部署的成本,部署Nacos的时候直接打包Nacos镜像就好,不需要再单独部署一套数据库。1.3之前是MySQL集群做持久化。
-
Nacos的热加载
负载均衡-Ribbon
Ribbon是一款客户端负载均衡器(消费者内部持有的负载均衡器)。Ribbon已经被Spring Cloud官方技术整合,运行时以SDK形式内嵌到每个微服务的实例中。
- Ribbon执行过程
- 服务A与服务B实例在启动时向 Nacos 注册;
- 服务A向服务B发起通信前,Ribbon 向 Nacos 查询商品服务的可用实例列表;
- Ribbon 根据设置的负载策略从服务B可用实例列表中选择实例;
- 服务A实例向服务B实例发起请求,完成 RESTful 通信;
负载均衡策略
- RoundRobinRule:
轮询策略,Ribbon 默认策略。默认超过 10 次获取到的 server 都不可用,会返回⼀个空的 server。
-
RandomRule:
随机策略,如果随机到的 server 为 null 或者不可用的话。会不停地循环选取。 -
RetryRule:
重试策略,⼀定时限内循环重试。默认继承 RoundRobinRule,也⽀持自定义注⼊,RetryRule 会在每次选取之后,对选举的 server 进⾏判断,是否为 null,是否 alive,并且在 500ms 内会不停地选取判断。而 RoundRobinRule 失效的策略是超过 10 次,RandomRule 没有失效时间的概念,只要 serverList 没都挂。
- BestAvailableRule:
最小连接数策略,遍历 serverList,选取出可⽤的且连接数最小的⼀个 server。那么会调用 RoundRobinRule 重新选取。
- AvailabilityFilteringRule:
可用过滤策略。扩展了轮询策略,会先通过默认的轮询选取⼀个 server,再去判断该 server 是否超时可用、当前连接数是否超限,都成功再返回。
- ZoneAvoidanceRule:
区域权衡策略。扩展了轮询策略,除了过滤超时和链接数过多的 server,还会过滤掉不符合要求的 zone 区域⾥⾯的所有节点,始终保证在⼀个区域/机房内的服务实例进行轮询。
所有的负载均衡策略都在com.netflix.loadbalancer包下的类中
- 修改负载均衡策略
服务通信-OpenFeign
Feign和OpenFeign
Netflix Feign 是 Netflix 设计的开源的声明式 WebService 客户端,用于简化服务间通信。Netflix Feign 采用“接口+注解”的方式开发,通过模仿 RPC 的客户端与服务器模式(CS),采用接口方式开发来屏蔽网络通信的细节。 而OpenFeign并不是独立技术,底层基于Netflix Feign,在其基础上进行了封装。结合了原有Spring MVC的注解,所以使用OpenFeign的开发方法和Spring MVC Controller比较相似。
OpenFeign使用
-
创建服务提供者
创建一个普通的spring cloud服务(不废话了)
<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>
复制代码
- 创建消费者项目并且引入核心依赖
<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>
<!-- openFeign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
复制代码
-
添加启动注解@EnableFeignClients
@EnableFeignClients 注解,其含义为通知 Spring 启用 OpenFeign 声明式通信。 -
创建通信接口
创建一个client包,用于保存请求的客户端和通信接口,@FeignClient说明当前接口是OpenFeign通信客户端,参数值是服务提供者的服务id。
消费者服务的通信接口返回Customer类型和提供者服务的接口返回类型可以不一样。如果在请求成功后,反序列化时,只会反序列化两者共有的属性。
- 注入client
OpenFeign配置事项
-
OpenFeign可修改负载均衡策略(同Ribbon方法一样)
-
OpenFeign的数据压缩功能
压缩功能的配置也是在消费服务端配置,在发送请求是告诉提供者是否需要数据压缩,在 OpenFeign 中,默认并没有开启数据压缩功能。但如果你在服务间单次传递数据超过 1K 字节,强烈推荐开启数据压缩功能。默认 OpenFeign 使用 Gzip 方式压缩数据,对于大文本通常压缩后尺寸只相当于原始数据的 10%~30%,这会极大提高带宽利用率。但是对于计算密集型,CPU 负载长期超过 70%,因数据压缩、解压缩都需要 CPU 运算,开启数据压缩功能反而会给 CPU 增加额外负担。
-
替换默认通信组件
OpenFeign 默认使用 Java 自带的 URLConnection 对象创建 HTTP 请求,通信组件可以更换为 Apache HttpClient、OKHttp 这样的专用通信组件,基于这些组件自带的连接池,可以更好地对 HTTP 连接对象进行重用与管理。
(1)引入okhttp通讯组件依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>11.0</version>
</dependency>
复制代码
(2)启动类中初始化OKHTTPClient对象
@SpringBootApplication
@EnableFeignClients // 启动OpenFeign
public class ConsumerOpenfeignApplication {
//Spring IOC容器初始化时构建okHttpClient对象
@Bean
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
//读取超时时间
.readTimeout(10, TimeUnit.SECONDS)
//连接超时时间
.connectTimeout(10, TimeUnit.SECONDS)
//写超时时间
.writeTimeout(10, TimeUnit.SECONDS)
//设置连接池
.connectionPool(new ConnectionPool())
.build();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerOpenfeignApplication.class, args);
}
}
复制代码
(3)启动OkHttp配置
API网关-Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 自己开发的新一代 API 网关产品。它基于 NIO 异步处理,摒弃了 Zuul 基于 Servlet 同步通信的设计,因此拥有更好的性能。同时,Spring Cloud Gateway 对配置进行了进一步精简,比 Zuul 更加简单实用。(Zuul第一代spring cloud 的网关服务,现在已经嗝屁了。停止维护了)
Gateway有哪些作用
在用户端与微服务之间建立了一道屏障,通过 API 网关为微服务访问提供了统一的访问入口,所有用户端的请求被 API 网关拦截并在此基础上可以实现额外功能。
-
针对所有请求进行统一鉴权、熔断、限流、日志等前置处理,让微服务专注自己的业务。
-
统一调用风格,通常 API 网关对外提供 RESTful 风格 URL 接口。用户传入请求后,由 API 网关负责转换为后端服务需要的 RESTful、RPC、WebService 等方式,这样便大幅度简化用户的接入难度。
-
更好的安全性,在通过 API 网关鉴权后,可以控制不同角色用户访问后端服务的权利,实现了服务更细粒度的权限控制。
-
API 网关是用户端访问 API 的唯一入口,从用户的角度来说只需关注 API 网关暴露哪些接口,至于后端服务的处理细节,用户是不需要知道的。从这方面讲,微服务架构通过引入 API 网关,将用户端与微服务的具体实现进行了解耦。
gateway配置
-
Route(路由):是指一个完整的网关地址映射与处理过程,一个完整的路由包含两部分配置,Predicate(谓词)和 Filter(过滤器)
-
常见配置实例
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: false #不再需要Gateway路由转发
routes: #路由规则配置
#第一个路由配置,service-customer路由规则
- id: service_customer_route #路由唯一标识
#lb开头代表基于gateway的负载均衡策略选择实例
uri: lb://service-customer
#谓词配置
predicates:
#Path路径谓词,代表用户端URI如果以/customer开头便会转发到service-customer实例
- Path=/customer/**
#After生效时间谓词,2020年10月15日后该路由才能在网关对外暴露
- After=2020-10-05T00:00:00.000+08:00[Asia/Shanghai]
#谓词配置
filters:
#忽略掉第一层前缀进行转发
- StripPrefix=1
#为响应头附加X-Response=Blue
- AddResponseHeader=X-Response,Blue
#第二个路由配置,service-contact路由规则
- id: service_contact_route
uri: lb://service-contact
predicates:
- Path=/contact/**
filters:
- StripPrefix=1
复制代码
具体配置可以看官方文档 : docs.spring.io/spring-clou…
-
Spring cloud gateway执行原理
-
Spring Cloud Gateway 启动时基于 Netty Server 监听指定的端口(该端口可以通过 server.port 属性自定义)。当前端应用发送一个请求到网关时,进入 Gateway Handler Mapping 处理过程,网关会根据当前 Gateway 所配置的谓词(Predicate)来决定是由哪个微服务进行处理。
-
确定微服务后,请求向后进入 Gateway Web Handler 处理过程,该过程中 Gateway 根据过滤器(Filters)配置,将请求按前后顺序依次交给 Filter 过滤链进行前置(Pre)处理,前置处理通常是对请求进行前置检查,例如:判断是否包含某个指定请求头、检查请求的 IP 来源是否合法、请求包含的参数是否正确等。
-
当过滤链前置(Pre)处理完毕后,请求会被 Gateway 转发到真正的微服务实例进行处理,微服务处理后会返回响应数据,这些响应数据会按原路径返回被 Gateway 配置的过滤链进行后置处理(Post),后置处理通常是对响应进行额外处理,例如:将处理过程写入日志、为响应附加额外的响应头或者流量监控等。
自定义全局过滤器
public class ElapsedFilter implements GlobalFilter, Ordered {
//起始时间属性名
private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";
/**
* 实现filter()方法记录处理时间
*
* @param exchange 用于获取与当前请求、响应相关的数据,以及设置过滤器间传递的上下文数据
* @param chain Gateway过滤器链对象
* @return Mono对应一个异步任务,因为Gateway是基于Netty Server异步处理的,Mono对就代表异步处理完毕的情况。
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//Pre前置处理部分
//在请求到达时,往ServerWebExchange上下文环境中放入了一个属性elapsedTimeBegin,保存请求执行前的时间戳
exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());
//chain.filter(exchange).then()对应Post后置处理部分
//当响应产生后,记录结束与elapsedTimeBegin起始时间比对,获取RESTful API的实际执行时间
return chain.filter(exchange).then(
Mono.fromRunnable(() -> { //当前过滤器得到响应时,计算并打印时间
Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);
if (startTime != null) {
log.info(exchange.getRequest().getRemoteAddress() //远程访问的用户地址
+ " | " + exchange.getRequest().getPath() //Gateway URI
+ " | cost " + (System.currentTimeMillis() - startTime) + "ms"); //处理时间
}
})
);
}
//设置为最高优先级,最先执行ElapsedFilter过滤器
//return Ordered.LOWEST_PRECEDENCE; 代表设置为最低优先级
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
复制代码
- 配置信息
spring:
application:
name: gateway #配置微服务id
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos通信地址
username: nacos
password: nacos
gateway: #让gateway通过nacos实现自动路由转发
discovery:
locator:
enabled: true #locator.enabled是自动根据URL规则实现路由转发
# routes: #路由规则配置
# #第一个路由配置,service-a路由规则
# - id: service_a_route #路由唯一标识
# #lb开头代表基于gateway的负载均衡策略选择实例
# uri: lb://gateway-test
# #谓词配置
# predicates:
# #Path路径谓词,代表用户端URI如果以/a开头便会转发到service-a实例
# - Path=/gateway-test/**
# #谓词配置
# filters:
# #忽略掉第一层前缀进行转发
# - StripPrefix=1
server:
port: 9007 #服务端口号
management:
endpoints:
web:
exposure:
include: '*' #对外暴露actuator所有监控指标,便于监控系统收集跟踪
复制代码
- 通过网关请求接口
http://127.0.0.1:9007/gateway-test/test
复制代码
gateway是请求目标服务的id,通过nacos自动路由,或者自定义路由的id
- 过滤器的处理日志
c.t.gatewayservice.filter.ElapsedFilter : /127.0.0.1:52606 | /gateway-test/test | cost 9ms
复制代码
Alibaba Sentinel
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。
Sentinel官网
搭建Sentinel Dashboard
搭建Sentinel Dashboard可以从github上直接下载可以运行jar,也可以下源码
Sentinel GitHub地址
运行对应版本的jar和指定端口
java -jar -Dserver.port=9100 sentinel-dashboard-1.8.1.jar
复制代码
访问ip+端口(例如:127.0.0.1:9100)进入Dashboard登录页面,账号密码都是sentinel;
创建一个Sentinel 客户端demo,并且让Sentinel监控
server:
port: 9009
spring:
application:
name: sample-sentinel-client
cloud:
nacos:
discovery:
namespace: public
password: nacos
server-addr: 127.0.0.1:8848
username: nacos
sentinel:
eager: true
transport:
dashboard: 127.0.0.1:9100#监控的Sentinel地址
复制代码
服务成功地完成通信
配置限流规则
-
本地创建一个测试请求
-
配置流控规则
sentinel-dashboard是基于懒加载的,只有访问过得功能url才会在簇点链路菜单中出现
-
直接模式:当 list 接口 QPS 超过 1个时限流,浏览器会出现“Blocked by Sentinel”。
-
关联模式:当同 list 接口关联的check 接口 QPS 超过 1 时,再次访问list 接口便会响应“Blocked by Sentinel”。
-
链路模式:链路模式相对复杂,比如开发了一个单机的电商系统,为了满足完成“下订单”的业务,程序代码会依次执行订单创建方法->减少库存方法->微信支付方法->短信发送方法。方法像链条一样从前向后依次执。将入口资源设为“/check”,则只会针对 check 接口的调用链路生效。当访问 check 接口的QPS 超过 1 时,List 接口就会被限流。而另一条链路从 scan 接口到List 接口的链路则不会受到任何影响。链路模式与关联模式最大的区别是 check 接口与 List 接口必须是在同一个调用链路中才会限流,而关联模式是任意两个资源只要设置关联就可以进行限流。
- 快速失败:是指流浪当过限流阈值后,直接返回响应并抛出 BlockException,快速失败是最常用的处理形式
- Warm Up: 用于应对瞬时大并发流量冲击。当遇到突发大流量 Warm Up 会缓慢拉升阈值限制,预防系统瞬时崩溃,这期间超出阈值的访问处于队列等待状态,并不会立即抛出 BlockException
- 排队等待:是采用匀速放行的方式对请求进行处理。如下图规则,如果有100个请求,单机阈值是4,代表250ms会匀速放行1个请求。如果某个请求超过了1000ms就会直接抛出BlockExcption。(匀速队列只支持QPS模式,且单机阈值不能超过1000)
- 验证流控
熔断降级配置
微服务的熔断是指在某个服务接口在执行过程中频繁出现故障的情况,我们便认为这种状态是“不可接受”的,立即对当前接口实施熔断。在规定的时间内,所有送达该接口的请求都将直接抛出 BlockException,在熔断期过后新的请求进入看接口是否恢复正常,恢复正常则继续运行,仍出现故障则再次熔断一段时间,以此往复直到服务接口恢复。
-
慢调用比例
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
-
异常比例
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% – 100%。
-
异常数
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。