前言
- 介绍Nacos作为注册中心的基本使用
- 介绍Nacos作为配置中心的基本使用
- 介绍Feign的微服务远程调用(非常实用)
- 服务两端统一接口实现
- 服务接口统一抽取
- 介绍SpringCloudGateway网关的基本使用
- 路由断言
- 请求过滤器
认识Nacos
微服务注册中心,比Eureka功能更加丰富。
安装
- 必须配置JAVA_HOME
1 | export JAVA_HOME=/usr/local/java |
- application.properties里面可以修改端口
- 单机启动:
sh startup.sh -m standalone
- 访问:
http://ip:8848/nacos/index.html
- 用户名密码:nacos
Nacos服务注册
- 依赖
1 | <!-- 父工程中添加spring-cloud-alibaba管理依赖 --> |
- 客户端配置Nacos地址
1 | spring.cloud.nacos.server-asddr: {ip}:8848 # nacos 服务端地址 |
Nacos服务分级存储模型
- 服务跨集群调用问题:尽可能访问本地集群,本地集群不可用再访问外地集群
- 配置服务提供端集群属性
1 | spring: |
- 修改负载均衡规则
1 | userservice: |
分配权重
在Nacos控制台可以配置实例的权重
Namesapce环境隔离
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层的隔离。命名空间下的服务是独立的。(可以用来隔离测试环境啥的)
- 在nacos控制台创建命名空间
代码中配置命名空间:
spring.cloud.nacos.discovery.namespace: ${namespaceId}
总结
- namespace做环境隔离
- 每个namespace都有唯一id
- 不同namespace下的服务不可见
Nacos和Eureka的区别
- 相同点:
- 服务提供者注册服务信息
- 服务消费者定时拉取服务信息
- 支持服务提供者心跳方式做健康监测
- 不同点:
- Nacos会将服务提供者划分为临时实例和非临时实例
- 临时实例采用心跳检测,心跳停止直接删除服务
- 非临时实例,nacos主动询问实例,不会删除服务
- 消费者定时服务拉取,当消息变更,nacos注册中心会主动像消费者进行服务列表同步
- Eureka只做服务拉取,不做同步
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
- Nacos会将服务提供者划分为临时实例和非临时实例
- 配置非临时实例
1 | false = |
Nacos配置管理
统一配置管理
基础知识
- 配置更改热更新,保证配置变化不用重启服务
- 配置步骤
- 登录nacos管理页面
- 新建配置管理
- DataId对为配置名称:
userservice-dev.yaml
- 编写配置:一般用来做一些开关,或者ABTest等
- DataId对为配置名称:
- 启动流程
- 项目启动 —- 读取nacos配置文件 —- 读取本地配置文件application.yml —- 创建spring容器 —- 加载bean
- 但是如果nacsos地址在application里面,那么这个流程就不成立了
- 建立一个bootstrap.yml,供项目启动时读取nacos地址等(bootstrap是引导文件,会在启动后立刻加载)
依赖
1 | <!--客户端配置 nacos 配置中心 --> |
1 | # bootstrap.yml |
1 | // 这里的路径就是在nacos中配置的内容(可以理解为,nacos中是在远程的配置文件) |
nacos中设置配置总结
- 在Nacos管理页面中添加配置文件
- 在微服务中引入nacos的config依赖
- 在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。(这些决定了程序启动时去读取nacos中哪个配置文件)
配置热更新
方式
两种方式实现配置自动更新:
- 在用到配置的类上加
@RefreshScope
注解
1 | 4j |
- 使用
@ConfigurationProperties
:不需要RefreshScope也可以自动刷新
1 | // 定义配置类 |
问题
编写代码遇到问题,客户端不停地去请求配置中心,并打印日志,1秒估计得有几十次。
答:查阅了资料,好像是因为服务端MD5和客户端MD5不一致,但是我不同意,因为功能都能实现。我猜是nacos1.4.1版本在macos上导致的日志打印问题。更新了nacos1.4.3版本后问题解决。
总结
- 通过@Value和@RefreshScope实现热更新
- 通过@ConfigurationProperties注入
- 不是所有配置都适合放在配置中心,维护起来很麻烦
配置共享
一个服务集群可能会共享一些配置。
原理
微服务启动时会从nacos读物多个配置文件:
- [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
- [spring.application.name].yaml,例如:userservice.yaml
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此在这个配置项中配置共享。
注意【多服务共享配置】
- 线上配置 > 本地配置
- 线上当前环境配置 > 线上共享配置
搭建Nacos集群
- 搭建数据库集群
- 不同机器nacos配置集群和数据库集群的地址,username和pwd
- ngnix反向代理
Feign远程调用
RestTemplate的问题
- 代码可读性差,编程体验感差
- 参数复杂,URL难以维护
很不优雅。
Feign使用
Feign是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送。
依赖
1 | <!-- Feign --> |
编码
1 | // 1. 启动类加注解 |
注意
Feign中已经集成了Ribbon,实现了负载均衡,非常的优雅。
自定义Feign配置
Feign运行自定义配置来覆盖默认配置,可以修改的配置大致如下:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | NONE, BASIC(请求基本信息), HEADERS, FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 指定请求参数的编码格式 |
feign.Contract | 支持的注解格式 | 默认SpringMVC |
feign.Retryer | 失败的重试机制 | 请求失败的重试机制,默认没有,可以使用Ribbon的重试 |
一般配置日志就行。
实践
- 配置文件修改
1 | # 全局 |
- 代码配置
1 | // 1. 声明配置文件 |
注意:局部配置覆盖全局。
Feign性能优化
底层的客户端实现
- URLConnection:默认实现,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
性能优化:使用有连接池的实现,以及日志级别为basic和none
实践
- 依赖
1 | <!-- httpclient --> |
- 配置连接池
1 | feign: |
Feign的最佳实践
方式一:继承
给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
1 | public interface UserAPI { |
缺点:
- 服务紧耦合
- 父接口参数列表中的映射不会被继承
方式二:抽取
将FeignClient抽取为独立模块,并且把接口相关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
提供者把接口抽取出来,变成jar依赖,给消费者引用。
缺点:
- 消费者可能引入过多没有必要的依赖
方式二实践!非常重要!
- 首先创建一个module,命名为feign-api,然后引入feign的starter依赖
- 将pojo,feignConfiguration(之前用来配置日志的),client复制到feign-api中
- 在order-service中引用feign-api的依赖
- 修改order-service中的import
- 引入依赖
1 | <dependency> |
问题
当定义的FeignClients不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种解决方案。
- 指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
- 定向指定字节码
@EnableFeignClients(clients = {UserClient.class})
- 指定FeignClient所在包
补充
maven安装外部jar包!!
1 | mvn install:install-file -Dfile=${jar包绝对路径} -DgroupId=com.memoforward -DartifactId=xxx-sdk -Dversion=1.1 -Dpackaging=jar -DgeneratePom=true |
1 | <dependency> |
Gateway服务网关
为什么需要网关
痛点:不是所有微服务接口都是可以被访问的,需要对请求进行校验
网关功能:
- 进行身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
网关的技术实现
SpringCloud中网关的实现包括两种:
- gateway
- zuul
Zuul基于Servlet的实现,属于阻塞式变成。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
搭建网关的步骤
- 依赖
1 | <!-- nacos服务注册,网关自己就是一个服务 --> |
- 基础配置
1 | server: |
路由断言工厂(配置)
- 我们在配置文件中写的断言规则只是字符串,这些字符串会被断言工厂读取并处理,转变为路由判断的条件
- 例如
Path=/user/**
是按照路径匹配,这个规则是由PathRoutePredicateFactory
类来处理的 - 像上述的断言工厂Spring中还有11个
Spring的官网中有各种断言示例:SpringGateway断言。
过滤器
路由过滤器
GateWayFilter是网关中提供一种过滤器,可以对进入网关的请求和微服务返回的响应做处理。
可以形成过滤器链,用来对请求进行操作,比如添加请求头、限制请求流量。
- 配置过滤器:Spring中官网各种过滤器的示例:SpringGateway过滤器
全局过滤器
- 自定义全局过滤器:实现
GlobalFilter
接口
1 | public interface GlobalFilter { |
简单案例:
1 |
|
过滤器执行顺序
三类过滤器:路由过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前的这个三种过滤器合并到一个链中,依次执行每个过滤器。
顺序:
- 路由过滤器和defaultFilter的order由Spring指定,默认从1开始递增
- 当order一样,会按照defaultFilter > 路由过滤器 > GlobalFilter的顺序执行
跨域问题处理
跨域问题:浏览器禁止请求的发起者与服务端发生跨域的ajax请求,请求被浏览器拦截
解决方案:CORS
- Gateway配置跨域
1 | spring: |