SpringCloud简介Spring Cloud组成服务的拆分与远程调用根据订单id查询订单功能★Eureka注册中心Eureka的原理Eureka的作用搭建EurekaService注册服务拉取服务★Ribbon负载均衡ribbon具体实现流程IRule接口调整负载均衡规则饥饿加载负载均衡的作用★Nacos注册中心Nacos服务分级存储模型如何配置实例的集群属性根据权重负载均衡环境隔离(namespace)Nacos注册中心原理CAP理论★Nacos配置管理配置热部署多环境共享配置★Http客户端FeignFeign的介绍Feign的使用Feign的配置Feign的性能优化Feign的最佳实践★Gateway统一网关搭建网关服务路由断言工厂路由过滤器全局过滤器跨域问题处理
Spring Cloud是一系列框架的有序集合,它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署
Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
Spring Cloud 的子项目,大致可分成两类,一类是对现有成熟框架“Spring Boot化”的封装和抽象,也是数量最多的项目
第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色
对于我们想快速实践微服务的开发者来说,第一类子项目就已经足够使用
Spring Cloud Netflix
Spring Cloud Config
Spring Cloud Stream
Spring Cloud Security
Spring Cloud Zookeeper
Spring Cloud Eureka
SpringCloud
SpringCloud是目前国内使用最广泛的微服务框架
官网地址:https://spring.io/projects/spring-cloud
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验
服务注册发现:
Eureka,Nacos,Consul
服务远程调用:
OpenFeign,Dubbo
服务链路监控:
Zipkin,Sltuth
统一配置管理:
SpringCloudConfig,Nacos
统一网关路由:
SpringCloudGateway,Zuul
流控,降级,保护:
Hystix,Sentinel
SpringCloud 与 SpringBoot 版本兼容性
Release Train | Boot Version |
---|---|
2021.0.x aka Jubilee | 2.6.x, 2.7.x (Starting with 2021.0.3) |
2020.0.x aka Ilford | 2.4.x, 2.5.x (Starting with 2020.0.3) |
Hoxton | 2.2.x, 2.3.x (Starting with SR5) |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
1、不同微服务,不要重复开发相同业务
2、微服务数据独立,不要访问其它微服务的数据库
3、微服务可以将自己的业务暴露为接口,供其它微服务调用
SpringCloud坐标
391 <parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-parent</artifactId>
4 <version>{spring-boot-docs-version}</version>
5 <relativePath /> <!-- lookup parent from repository -->
6 </parent>
7
8<dependencyManagement>
9 <dependencies>
10 <dependency>
11 <groupId>org.springframework.cloud</groupId>
12 <artifactId>spring-cloud-dependencies</artifactId>
13 <version>{spring-cloud-version}</version>
14 <type>pom</type>
15 <scope>import</scope>
16 </dependency>
17 </dependencies>
18</dependencyManagement>
19
20<dependencies>
21 <dependency>
22 <groupId>org.springframework.cloud</groupId>
23 <artifactId>spring-cloud-starter-config</artifactId>
24 </dependency>
25 <dependency>
26 <groupId>org.springframework.boot</groupId>
27 <artifactId>spring-boot-starter-test</artifactId>
28 <scope>test</scope>
29 </dependency>
30</dependencies>
31
32<build>
33 <plugins>
34 <plugin>
35 <groupId>org.springframework.boot</groupId>
36 <artifactId>spring-boot-maven-plugin</artifactId>
37 </plugin>
38 </plugins>
39</build>
需求:根据订单id查询订单的同时,把订单所属的用户信息一起返回
根据微服务的原则,我们只能从订单服务向用户服务发起远程调用来获取用户信息,符合单一职责原则
如何在java代码中发出http请求?
注册RestTemplate
在order-service的OrderApplication启动类中注册RestTemplate
171
2public class OrderApplication {
3
4 public static void main(String[] args) {
5 SpringApplication.run(OrderApplication.class, args);
6 }
7
8 /**
9 * 创建restTemplate并且注入容器
10 * @return
11 */
12
13 public RestTemplate restTemplate(){
14 return new RestTemplate();
15 }
16
17}
restTemplate
采用restful风格的http请求,有get,post,put,delete等方法
获取数据方法:getForObject()
参数:
url:请求路径
responseType:返回值类型字节码
接收到的数据都是Json风格,但是我们可以在第二个设置返回值类型,rest模板会自动反序列化数据为对象
271
2public class OrderService {
3
4
5 private OrderMapper orderMapper;
6
7
8 private RestTemplate restTemplate;
9
10 public Order queryOrderById(Long orderId) {
11 // 1.查询订单
12 Order order = orderMapper.findById(orderId);
13
14 // 2.利用restTemplate发起http请求,查询用户
15 // 2.1.url路径
16 String url = "http://localhost:8081/user/"+ order.getUserId();
17
18 // 2.2.发起http请求,实现远程调用
19 User user = restTemplate.getForObject(url, User.class);
20
21 //封装user到order
22 order.setUser(user);
23
24 // 4.返回
25 return order;
26 }
27}
之前我们的订单服务访问用户服务,是通过一次http请求获取到的数据,我们是将userService的ip和视图名称硬编码在了程序中
服务调用关系
服务的提供者:暴露接口给其它微服务调用
服务消费者:调用其它微服务提供的接口
提供者与消费者角色其实是相对的,是根据业务环境决定的
一个服务可以同时是服务提供者和服务消费者
服务消费者和服务提供者都作为eureka-client存在
每个服务在启动时都会将自身的信息注册给eureka,每个微服务都可能成为服务提供者
服务消费者在需要其它服务的时候会从注册中心拉取服务信息
注册中心会返回该服务机器的所有注册地址,通过负载均衡的方式挑选最佳的服务地址
服务消费者向服务地址发起远程调用(服务提供者每隔30秒向eureka发送一次存活状态,确保服务存在,健康状态确认)
记录和管理微服务
消费者该如何获取服务提供者具体信息?
服务提供者启动时向注册中心注册信息
注册中心保存信息
消费者根据服务名称向注册中心拉取服务提供者信息
如果有多个服务提供者,消费者该如何选择?
服务消费者利用负载均衡算法,从服务列表中挑选
消费者如何感知服务提供者健康状态?
服务提供者每隔30秒向注册中心发送心跳请求,报告健康状态
注册中心会更新记录服务列表信息,不健康的服务会被列表剔除
消费者可以拉取到最新的信息
搭建步骤:
1、创建项目,引入spring-cloud-starter-netflix-eureka-server
的依赖,交由springboot自动装配
41<dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
4</dependency>
2、编写启动类,添加@EnableEurekaService注解,开启eureka服务
111
2
3public class EurekaApplication {
4
5 public static void main(String[] args) {
6
7 SpringApplication.run(EurekaApplication.class,args);
8
9 }
10
11}
3、添加application.properties文件,编写配置 (本篇使用到的properties配置也可以使用yaml配置替换)
eureka在注册信息的时候会将自己本身作为一个微服务注册到注册中心,因此也要提供微服务名称与eureka地址,方便将来eureka集群通讯
61#服务端口
2server.port=10086
3#微服务名称
4spring.application.name=eurekaserver
5#eureka地址
6eureka.client.service-url.defaultZone:http://127.0.0.1:10086/eureka
查看eureka注册中心状态
Instances currently registered with Eureka
查看Application服务实例列表,即注册的微服务
Status:UP表示服务存活,DOWN表示服务死亡 + 服务IP地址
将user-service服务注册到EurekaService步骤如下:
1、在user-service项目引入spring-cloud-starter-netflix-eureka-client
的依赖
41<dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
4</dependency>
2、在application.properties文件,编写下面的配置
21spring.application.name=userservice
2eureka.client.service-url.defaultZone:http://127.0.0.1:10086/eureka
通过idea提供的服务拷贝功能拷贝user服务,CopyConfiguration,在环境VM option中配置新的端口号-Dserver.port=8082
,启动服务
可以在注册中心发现两个服务UP(2)
服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡
实现拉取的两种方法:
1、修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口
xxxxxxxxxx
11String url = "http://userservice/user/"+ order.getUserId();
2、在order-service项目的启动类OrderApplication中的RestTemplate实例注册函数上添加负载均衡注解:
@LoadBalanced注解 告诉restTemplate请求需要被ribbon拦截处理
xxxxxxxxxx
111 /**
2 * 创建restTemplate并且注入容器
3 * @return
4 */
5
6
7 public RestTemplate restTemplate(){
8 return new RestTemplate();
9 }
10
11}
总结:搭建EurekaServer
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现
通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用
Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中
因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具,所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要
负载均衡流程
1、order-service先发起请求http://userservice/user/1
2、Ribbon拿到服务名称后会去找eureka-server拉取userservice的服务列表
3、eureka-service返回服务名称对应的服务列表
4、Ribbon根据负载均衡轮循找到对应服务
LoadBalancerInterceptor类
实现了ClientHttpRequestInterceptor接口
由restTemplate发起的请求在被LoadBalanced注解修饰后会经过这个接口的实现类
拦截器会在底层通过rule对象choose(key)
方法决定要以何种负载均衡规则来选择列表中的服务
IRule接口
AbstractLoadBalancerRule抽象类
AbstractLoadBalancerRule类是负载均衡策略IRule的抽象实现类,在该抽象类中定义了负载均衡器ILoadBalancer对象
该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并依次设计一些算法来针对特定场景的高级策略
RoundRobinRule 轮循调度负载均衡规则
直接获取下一个可用实例,如果超过10次没有获取到可用的实例,则返回空且打印异常信息
RandomRule 随机选择服务规则
通过chooseRandomInt方法获取一个随机数,该随机数作为可用服务列表的索引来获取具体的实例
1、请求会被LoadBalancerInterceptor(负载均衡拦截器)拦截
2、拦截下的请求会交由RibbonLoadBalancerClient(客户端负载均衡器)进行处理,获取url中的服务名称
3、然后通过DynamicServerListLoadBalancer(动态服务列表负载均衡器)获取到eureka中的服务列表信息
4、再去找IRule接口实现的负载均衡规则来获取到服务资源,返回给客户端负载均衡器
5、最后修改url发送请求
继承关系图
负载均衡策略
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器,它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>、<clientConfigNameSpace>、ActiveConnectionsLimit 属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
通过定义IRule实现可以修改负载均衡规则,有两种方式:
两种负载均衡机制的作用范围是不同的
1、代码方法:在order-service中的OrderApplication类中,添加一个新的IRule实现类
全局配置
xxxxxxxxxx
61
2public IRule randomRule(){
3
4 return new RandomRule();
5
6}
2、配置文件方式:在order-service的application.properties文件中,添加新的配置也可以修改规则
指定服务名称配置
xxxxxxxxxx
21#指定服务负载均衡规则
2userserver.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalancerClient,请求时间会很长
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
xxxxxxxxxx
41#开启饥饿加载
2ribbon.eager-load.enabled=true
3#指定饥饿加载的服务名称
4ribbon.eager-load.clients=orderserver
如果有多个使用数组下标添加clients[0]=xxserver
、clients[1]=xxserver
如果是yml中加换行加- xxserver
解决并发压力,提高应用处理性能(增加吞吐量,加强网络处理能力)
提供故障转移,实现高可用
通过添加或减少服务器数量,提供网站伸缩性(扩展性)
安全防护(负载均衡设备上做一些过滤,黑白名单等处理)
总结:
1.Ribbon负载均衡规则
2.负载均衡自定义方式
3.饥饿加载
认识Nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件,相比Eureka功能更加丰富,在国内受欢迎度较高
Nacos官网:https://nacos.io/zh-cn/
下载nacos
在cmd中以单体模式启动 startup.cmd -m standalone
访问nacos终端控制器:http://192.168.119.1:8848/nacos/index.html#/login
用户名和密码都是nacos
Spring Cloud Commons features:
DiscoveryClient
interfaceServiceRegistry
interfaceRestTemplate
to resolve hostnames using DiscoveryClient
由于注册中心都实现了以上springCloud接口规范,因此我们只需要对配置进行修改即可
配置流程
1、在cloud-demo父工程中添加spring-cloud-alilbaba的管理依赖
xxxxxxxxxx
81<!--nacos管理依赖-->
2<dependency>
3 <groupId>com.alibaba.cloud</groupId>
4 <artifactId>spring-cloud-alibaba-dependencies</artifactId>
5 <version>2.2.6.RELEASE</version>
6 <type>pom</type>
7 <scope>import</scope>
8</dependency>
注意:注释掉order-service和user-service中原有的eureka依赖
2、添加nacos的客户端依赖:
xxxxxxxxxx
51<!-- nacos客户端依赖 -->
2<dependency>
3 <groupId>com.alibaba.cloud</groupId>
4 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
5</dependency>
3、修改user-service和order-service中的application.properties文件,注释eureka地址,添加nacos地址:
xxxxxxxxxx
21#nacos服务地址
2spring.cloud.nacos.server-addr=localhost:8848
一个服务的多个实例会部署到多个机房,增高容灾性,一个地区的多个服务实例被成为集群
服务-集群-实例
服务跨集群调用问题
由于不同地域的服务访问存在网络延迟问题,因此服务调用尽可能选择本地集群的服务,跨集群调用延迟较高
只有在本地集群不可用的情况下,才会去访问其它集群
nacos引入集群的概念就是为了防止直接的跨集群调用问题
修改application.properties文件,添加下面配置
xxxxxxxxxx
21#配置集群名称,也就是机房位置,例如:HZ,杭州
2spring.cloud.nacos.discovery.cluster-name=HZ
开启对应的Nacos集群负载均衡配置
在order-server中配置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务
xxxxxxxxxx
21#配置负载均衡规则
2userserver.ribbon.NFLoadBalancerRuleClassName=com.alibaba.cloud.nacos.ribbon.NacosRule
Nacos负载均衡规则:在本地集群优先找到服务列表,在服务列表中随机选择可用服务
如果本地没有可用服务,那么就会发生跨集群访问服务,控制台会发出警告信息,提醒运维人员
总结
Nacos服务分级存储模型,提高了服务的容灾性
实际部署中会出现这样的场景:
Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高
如何配置机器权重?
在Nacos控制台可以设置实例的权重值(0~1),首先选中实例后面的编辑按钮
将权重设置为0.1,测试可以发现8081被访问到的频率大大降低
总结
Nacos控制台可以设置实例的权重值,0~1之间
同级群内的多个实例,权重越高被访问的频率越高
权重设置为0则完全不会被访问
namespace :nacos中服务存储和数据存储的最外层都是一个名为namespace(命名空间)的东西,用来做最外层隔离,不同命名空间之间的服务是无法互相访问的
Namespace >> Group >> Service/Data
在没设置命名空间的情况下,nacos上注册的服务都在public(保留命名空间)中
命名空间配置
管理员可以在nacos控制台创建新的命名空间分组
我们需要根据命名空间的id(自己设置或系统生成)来配置服务的命名空间
在application.properties中配置
xxxxxxxxxx
21#命名空间对应的id
2spring.cloud.nacos.discovery.namespace=d494b1c4-0822-4dbd-8dff-4aec25cdc97e
总结:
服务提供者有两种实例模式:
配置临时实例和非临时实例
xxxxxxxxxx
21#设置为非临时实例false,默认是临时实例true
2spring.cloud.nacos.discovery.ephemeral=false
总结:
1、Nacos与eureka的共同点
2、Nacos与Eureka的区别
CAP理论提出就是针对分布式数据库环境的
分布式系统的最大难点,就是各个节点的状态如何同步,CAP 定理是这方面的基本定理,也是理解分布式系统的起点
A (Availability) 可用性
C(Consistency) 一致性
P(Partition tolerance) 分区容错
配置更改热更新
配置管理服务会去通知微服务配置发生了改变,微服务会去向配置管理服务读取配置信息
新建配置
DataID:配置名称,不能重复,命名方式:服务名称+运行环境名称+配置类型后缀名
例如:userserver-dev.yaml
分组:默认即可
配置内容: 存放需要热更新需求的配置,比如日期格式,开关类型配置
微服务获取配置
默认配置获取步骤如下:
项目启动→读取本地配置文件application.yml→创建spring容器→加载bean
我们需要提前获取nacos的地址,才能去读取nacos中的配置文件,因此我们需要一个比application.yml
优先级更高的配置文件bootstrap.yml
来读取nacos地址
1、引入Nacos的配置管理客户端依赖
xxxxxxxxxx
51<!--nacos配置管理依赖-->
2<dependency>
3 <groupId>com.alibaba.cloud</groupId>
4 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
5</dependency>
2、在userserver中的resource目录添加一个bootstrap.properties文件,这个文件是引导文件,优先级高于application.properties
xxxxxxxxxx
101#服务名称
2spring.application.name=userserver
3#开发环境
4spring.profiles.active=dev
5#Nacos地址
6spring.cloud.nacos.server-addr=localhost:8848
7#文件后缀名
8spring.cloud.nacos.config.file-extension:yaml
9#配置文件所在命名空间
10spring.cloud.nacos.config.namespace: d494b1c4-0822-4dbd-8dff-4aec25cdc97e
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
作为配置文件id,来读取配置
3、配置自动刷新
Nacos中的配置文件变更后,微服务无需重启就可以感知,不过需要通过下面两种配置实现:
方式一: 在@Value注入的变量所在类上添加注解@RefreshScope
@RefreshScope 用来实现配置、实例热加载
方式二: 使用@ConfigurationProperties注解(推荐)
xxxxxxxxxx
141package cn.itcast.user.config;
2
3import lombok.Data;
4import org.springframework.boot.context.properties.ConfigurationProperties;
5import org.springframework.stereotype.Component;
6
7
8
9prefix = "pattern") (
10public class PatternProperties {
11
12 private String dateformat;
13
14}
通过聚合该类的方式在需要资源的类中引入配置文件资源
@ConfigurationProperties
注意事项:
不是所有的配置都适合放到配置中心,维护起来比较麻烦
建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置
总结:
1、在Nacos控制台终端添加署配置文件
2、在微服务中引入nacos配置的maven坐标
3、在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名,这些决定了程序启动时去nacos读取哪个文件
此多环境指的是同名服务的不同命名空间环境
微服务启动时会从nacos读取多个配置文件
我们使用服务名+配置文件后缀
的命名方式来配置多环境共享配置
例如在nacos终端创建新的配置userserver.yaml
,此配置会对所有环境下的userserver
服务进行全局配置
注意: 配置也具有环境变量namespace属性,此属性与微服务的环境变量属性不同,应该单独在bootstrap配置文件中指定,不指定默认为使用public环境下的配置文件
xxxxxxxxxx
21#此配置应该在bootstrap配置文件下配置
2spring.cloud.nacos.config.namespace: 配置文件id
关于配置优先级问题
多种配置的优先级:
单环境配置(服务名-profile.yaml) >> 多环境共享配置(服务名.yaml) >> 本地配置(application.yaml)
总结:
RestTemplate方法调用存在的问题
先来看我们以前利用RestTemplate发起远程调用的代码:
存在下面的问题:
Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
同时Feign在底层集成了Ribbon,会自动做到负载均衡
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题
在服务的pom文件中引入feign的依赖:
xxxxxxxxxx
51<!--feign客户端依赖-->
2<dependency>
3 <groupId>org.springframework.cloud</groupId>
4 <artifactId>spring-cloud-starter-openfeign</artifactId>
5</dependency>
在启动类上加上注解@EnableFeignClients
@EnableFeignClients springboot对Feign自动装配,启用feign
客户端
编写FeignClient接口
创建一个接口,作为客户端
@FeignClient注解 声明当前类是一个Feign客户端,用于微服务直接的调用
xxxxxxxxxx
141package cn.itcast.order.clients;
2
3import cn.itcast.order.pojo.User;
4import org.springframework.cloud.openfeign.FeignClient;
5import org.springframework.web.bind.annotation.GetMapping;
6import org.springframework.web.bind.annotation.PathVariable;
7
8"userserver") (
9public interface UserClient {
10
11 "/user/{id}") (
12 User findById( ("id")Long id);
13
14}
使用接口中定义的方法代替RestTemplate
在orderserver中测试
xxxxxxxxxx
151public Order queryOrderById(Long orderId) {
2
3 //查询订单
4 Order order = orderMapper.findById(orderId);
5
6 //用Feign远程调用
7 User user = userClient.findById(order.getUserId());
8
9 //封装user和order
10 order.setUser(user);
11
12 //返回
13 return order;
14
15}
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般我们需要配置的就是日志级别
自定义Feign的配置
方法一、配置文件方式配置:
全局生效
xxxxxxxxxx
21#如果用default就是全局配置,如果写服务名称,则是针对某个微服务的配置
2feign.client.config.default.loggerlevel=FULL
局部生效
xxxxxxxxxx
21#如果用default就是全局配置,如果写服务名称,则是针对某个微服务的配置
2feign.client.config.userserver.loggerlevel=FULL
方法二、java代码配置:
创建一个类FeignClientConfiguration
需要先声明一个Bean
xxxxxxxxxx
61public class FeignClientConfiguration {
2
3 public Logger.Level feignLogLevel(){
4 return Logger.Level.BASIC;
5 }
6}
如果是全局配置,则把它放到@EnableFeignClients这个注解中(启动类中)
xxxxxxxxxx
11defaultConfiguration = FeignClientConfiguration.class) (
如果是局部配置,则把它放到@FeignClient这个注解中(Feign客户端中)
xxxxxxxxxx
11value = "userserver", configuration = FeignClientConfiguration.class) (
Feign底层的客户端实现:
连接池:可以提前创建并存放多个连接对象
因此优化Feign的性能主要包括:
Feign添加HttpClient的支持:引入依赖
xxxxxxxxxx
51<!--httpClient-->
2<dependency>
3 <groupId>io.github.openfeign</groupId>
4 <artifactId>feign-httpclient</artifactId>
5</dependency>
配置http连接池:
xxxxxxxxxx
61#开启feign对HttpClient的支持
2feign.httpclient.enabled=true
3#最大的连接数
4feign.httpclient.max-connections=200
5#每个路径的最大连接数
6feign.httpclient.max-connections-per-route=50
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准(不推荐,会产生紧密耦合)
方法二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO,默认的Feign配置都放到这个模块中,提供给所有消费者使用
定义好feign-api
在API中定义好Client、实体类、默认配置等
引用依赖即可
抽取方式的Feign最佳实践
本质:maven的自动包装和导包
1、首先创建一个模块,命名为feign-api,然后引入feign的starter的依赖
2、创建Feign客户端,将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3、引依赖,在order-service中引入feign-api的依赖
4、导包,修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
5、启用Feign客户端,在order-service的启动类上添加注解,由于定义的FeignClient不在SpringBootApplication的扫描包范围,因此我们需要在指定FeignClient所在包加上注解@EnableFeignClients()
属性:
basePackages = "扫描包路径"
注入包下的所有客户端实例clients = {UserClient.class}
注入指定多个字节码(字节码数组)客户端实例
网关功能:
在SpringCloud中网关的实现包括两种:
Zuul是基于Servlet的实现,属于阻塞式编程,而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能
1、创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖
xxxxxxxxxx
111<!--网关依赖-->
2<dependency>
3 <groupId>org.springframework.cloud</groupId>
4 <artifactId>spring-cloud-starter-gateway</artifactId>
5</dependency>
6
7<!--nacos服务发现依赖-->
8<dependency>
9 <groupId>com.alibaba.cloud</groupId>
10 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
11</dependency>
添加gateway网关设置
xxxxxxxxxx
151#网关端口
2server.port=10010
3#服务名称
4spring.application.name=gateway
5#nacos地址
6spring.cloud.nacos.server-addr=localhost:8848
7#网关路由配置
8#路由id,自定义,只要唯一即可
9spring.cloud.gateway.routes[0].id=user-service
10#uri: http://127.0.0.1:8081 路由的目标地址http就是固定地址
11#路由的目标地址 lb(loadbalance)就是负载均衡,后面跟服务名称
12spring.cloud.gateway.routes[0].uri=lb://userservice
13#路由断言,也就是判断请求是否符合路由规则的条件
14# - Path=/user/** 这个是按照路径匹配,只要以/user/开头就符合要求
15spring.cloud.gateway.routes[0].predicates[0]=Path=/user/**
路由配置包括
访问流程
用户访问网关
网关通过路由规则判断,将请求路由到微服务
Route Predicate Factory
PathRoutePredicateFactory
类来处理的
Spring提供了11种基本的Predicate工厂:
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
Spring提供了31种不同的路由过滤器工厂,例如:
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
... | ... |
实例:
在文件中配置过滤器信息:
xxxxxxxxxx
11spring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=Key,This is value!
在userserver中测试:
xxxxxxxxxx
61"/{id}") (
2public User queryById( ("id") Long id, (value = "Key",required = false) String key) {
3
4 System.out.println("Key:"+key);
5
6 return userService.queryById(id);
通过网关访问userserver服务,可以获取到key的值
默认过滤器(对所有路由生效)
通过default-filters来配置
xxxxxxxxxx
11spring.cloud.gateway.default-filters[0]=AddRequestHeader=Key, This is value!
GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilte
r的作用一样
区别在于GatewayFilter
通过配置定义,处理逻辑是固定的,而GlobalFilter的逻辑需要自己写代码实现
定义方式是实现GlobalFilter接口
xxxxxxxxxx
121public interface GlobalFilter {
2
3 /**
4 * 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
5 *
6 * @param exchange 请求上下文,里面可以获取Request、Response等信息
7 * @param chain 用来把请求委托给下一个过滤器
8 * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
9 */
10 Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
11
12}
实例:
定义全局过滤器,拦截并判断身份
需求:拦截请求,判断请求参数是否含有access参数,参数值是否为true,同时满足则放行
过滤器需要设置顺序
@Order注解 设置过滤器优先级,值越小优先级越高
或者通过实现Ordered接口来设置
xxxxxxxxxx
301-1) (
2
3public class AccessFilter implements GlobalFilter {
4
5
6 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
7
8 //获取请求参数
9 ServerHttpRequest request = exchange.getRequest();
10 MultiValueMap<String, String> params = request.getQueryParams();
11
12 //获取参数中的access参数
13 String access = params.getFirst("access");
14
15 //获取值是否为true
16 if ("true".equals(access)){
17
18 //放行
19 return chain.filter(exchange);
20
21 }
22
23 //设置状态码,401
24 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
25
26 //拦截
27 return exchange.getResponse().setComplete();
28 }
29
30}
验证:
http://localhost:10010/user/1?access=true
可以访问
http://localhost:10010/user/1
状态401
过滤器类型
过滤器执行顺序
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
跨域:域名不一致就是跨域,主要包括:
域名不同: www.taobao.com
和 www.taobao.org
和 www.jd.com
和 miaosha.jd.com
域名相同,端口不同:localhost:8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
跨域资源共享(CORS)
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现:
xxxxxxxxxx
191spring
2 cloud
3 gateway
4 globalcors#全局的跨域处理
5 add-to-simple-url-handler-mapping true #解决options请求被拦截问题
6 cors-configurations
7 '[/**]'
8 allowedOrigins#允许哪些网站的跨域请求
9"http://localhost:8090"
10"http://www.leyou.com"
11 allowedMethods#允许的跨域ajax的请求方式
12"GET"
13"POST"
14"DELETE"
15"PUT"
16"OPTIONS"
17 allowedHeaders"*" #允许在请求中携带的头信息
18 allowCredentials true #是否允许携带cookie
19 maxAge 360000 #这次跨域检测的有效期