Spring Cloud

一、微服务架构

单体架构:将业务的所有功能集中在一个项

                  目中开发,打成一个包部署

分布式架构:根据业务功能对系统做拆分

                  每个业务功能模块作为独立项目

                  开发,称为一个服务

微服务架构

    特征:

单一职责:微服务拆分粒度更小,每一个服

                  务都对应唯一的业务能力,做到

                  单一职责

自治:团队独立、技术独立、数据独立,独

           立部署和交付

面向服务:服务提供统一标准的接口,与语

                  言和技术无关

隔离性强:服务调用做好隔离、容错、降级,

                  避免出现级联问题

二、 Spring Cloud

① Spring Cloud 是目前国内使用最广泛的微

     服务框架

② Spring Cloud 集成了各种微服务功能组件

     并基于 SpringBoot 实现了这些组件的

     动装配,从而提供了良好的开箱即用体验

③ Spring  Cloud 底层是依赖于 SpringBoot

     的,并且有版本的兼容关系

2. 五大组件

Eureka:注册中心

Zuul:服务网关

Ribbon:负载均衡

Feign:服务调用

Hystix:熔断器

三、服务拆分和远程调用

任何分布式架构都离不开服务的拆分,微

服务也是一样

 1. 服务拆分规则

① 不同微服务,不要重复开发相同业务

② 微服务数据独立,不要访问其它微服务

    的数据库

③ 微服务可以将自己的业务暴露为接口,

    供其它微服务调用

2. 提供者与消费者

在服务调用关系中,会有两个不同的角色:

服务提供者 (user-service):一次业务中,

被其它微服务调用的服务(提供接口给其它

微服务)

服务消费者 (order-service):一次业务中,

调用其它微服务的服务 (调用其它微服务提

供的接口)

其中,服务提供者与服务消费者的角色并不是

绝对的,而是相对于业务而言

四、Eureka 注册中心

1. Eureka 的作用

在 Eureka 架构中,微服务角色有两类:

EurekaServer:服务端,注册中心

        ① 记录服务信息

        ② 心跳监控

EurekaClient:客户端

        ① Provider:服务提供者

             注册自己的信息到 EurekaServer

             每隔30秒向 EurekaServer 发送心跳

        ② consumer:服务消费者

             根据服务名称从 EurekaServer 拉取

             服务列表

             基于服务列表做负载均衡,选中一个

             微服务后发起远程调用

2. 搭建 EurekaServer

① 创建项目,导入依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

② 编写启动类,添加 @EnableEurekaServer 注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
​
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class, args);}
}

③ 编写一个 application.yml 文件

server:port: 10086
spring:application:name: eureka-server
eureka:client:service-url: defaultZone: http://127.0.0.1:10086/eureka

④ 启动服务

启动微服务,然后在浏览器访问:http://127.0.0.1:10086 

3. 服务注册

将 user-service 注册到 eureka-server 中去

① 引入依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

② 配置文件

spring:application:name: userservice
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka

③ 启动多个 user-service 实例

4. 服务发现

将 order-service 的逻辑修改:向 eureka-server

拉取 user-service 的信息,实现服务发现

① 引入依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

② 配置文件

spring:application:name: orderservice
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka

③ 服务拉取和负载均衡

在 order-service 的 OrderApplication

中,给 RestTemplate 这个 Bean 添加

一个 @LoadBalanced 注解

修改 order-service 服务的 OrderService

类中的 queryOrderById 方法。修改访问

url 路径,用服务名代替 ip 、端口:

五、Ribbon 负载均衡

1. 负载均衡原理

SpringCloudRibbon 的底层采用了一个

拦截器,拦截了 RestTemplate 发出的

请求,对地址做了修改

2. 负载均衡策略

负载均衡的规则都定义在 IRule 接口中,

而 IRule 有很多不同的实现类: 

不同规则的含义如下:

其中默认的实现就是 ZoneAvoidanceRule,

是一种轮询方案

(2) 自定义负载均衡策略

通过定义IRule实现可以修改负载均衡规则,

但一般用默认的负载均衡规则,不做修改

有两种方式:

① 代码方式

在 order-service 中的 OrderApplication 类中,

定义一个新的IRule:

@Bean
public IRule randomRule(){return new RandomRule();
}

② 配置文件方式

在 order-service 的 application.yml 文件中,添

加新的配置也可以修改规则:

userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则 

3. 饥饿加载

Ribbon 默认是采用懒加载,即第一次访问时

才会去创建 LoadBalanceClient,请求时间会

很长

而饥饿加载则会在项目启动时创建,降低第一

次访问的耗时,通过下面配置开启饥饿加载:

ribbon:eager-load:enabled: trueclients: userservice

六、Nacos 注册中心

Nacos 是阿里巴巴的产品,现在是 SpringCloud

中的一个组件,相比 Eureka 功能更加丰富,在

国内受欢迎程度较高

Nacos 是 SpringCloudAlibaba 的组件,而

SpringCloudAlibaba 也遵循 SpringCloud 中定义

的服务注册、服务发现规范。因此使用 Nacos 和

使用 Eureka 对于微服务来说,并没有太大区别

主要差异在于:

① 依赖不同

② 服务地址不同

2. 服务注册到 Nacos

① 引入依赖

在父工程的 pom 文件中引入 SpringCloudAlibaba

的依赖:

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version><type>pom</type><scope>import</scope>
</dependency>

 然后在 user-service 和 order-service 中的 pom

文件中引入 nacos-discovery 依赖:

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

注意:不要忘了注释掉 eureka 的依赖

② 配置 nacos 地址

在 user-service 和 order-service 的 application.yml

中添加 nacos 地址:

spring:cloud:nacos:server-addr: localhost:8848

注意:不要忘了注释掉 eureka 的地址

③ 重启

 3. 服务分级存储模型

一级是服务

二级是集群

三级是实例

一个服务可以有多个实例,例如我们的

user-service,可以有:

127.0.0.1:8081

127.0.0.1:8082

127.0.0.1:8083

假如这些实例分布于全国各地的不同机房,

例如:

127.0.0.1:8081,在上海机房

127.0.0.1:8082,在上海机房

127.0.0.1:8083,在杭州机房

Nacos 就将同一机房内的实例划分为一个集群

也就是说,user-service 是服务,一个服务可

以包含多个集群,如杭州、上海,每个集群下

可以有多个实例,形成分级模型,如图:

微服务互相访问时,应该尽可能访问同集

群实例,因为本地访问速度更快,当本集

群内不可用时,才访问其它集群

(1) 给 user-service 配置集群

① 修改 user-service 的 application.yml 文件,

     添加集群配置:

spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZ # 集群名称

② 重启两个 user-service 实例后,我们可以在

    nacos 控制台看到下面结果:

 ③ 我们再次复制一个user-service启动配置,添加属性:

-Dserver.port=8083
-Dspring.cloud.nacos.discovery.cluster-name=SH

④ 启动 UserApplication3 后再次查看 nacos 控制台: 

 (2) 同集群优先的负载均衡

默认的 ZoneAvoidanceRule 并不能实现

根据同集群优先来实现负载均衡

因此 Nacos 中提供了一个 NacosRule

实现,可以优先从同集群中挑选实例

① 给 order-service 配置集群信息

修改 order-service 的 application.yml 文件,

添加集群配置:

spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZ # 集群名称

② 修改负载均衡规则

修改 order-service 的 application.yml 文件,

修改负载均衡规则:

userservice:ribbon:NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则

4. 权重配置

实际部署中会出现这样的场景:

服务器设备性能有差异,部分实例所在

机器性能较好,另一些较差,我们希望

性能好的机器承担更多的用户请求。

但默认情况下 NacosRule 是同集群内

随机挑选,不会考虑机器的性能问题

因此,Nacos提供了权重配置来控制访

问频率,权重越大则访问频率越高

在 nacos 控制台,找到 user-service 的实

例列表,点击编辑,即可修改权重:

 注:如果权重修改为 0,则该实例永远不会被访问

5. 环境隔离

Nacos 提供了 namespace 来实现环境隔离功能

nacos 中可以有多个 namespace,namespace 下

可以有 group、service 等,不同 namespace 之间

相互隔离

例如不同 namespace 的服务互相不可见

(1) 创建 namespace

默认情况下,所有 service、data、group 都在

同一个 namespace,名为 public:

① 我们可以点击页面新增按钮,添加一个namespace:

② 然后,填写表单:

③ 就能在页面看到一个新的 namespace:

(2) 给微服务配置 namespace

① 配置 namespace 只能通过修改配置来实现

例如,修改 order-service 的 application.yml

文件:

spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZnamespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID

② 重启 order-service 后,访问控制台,可以

    看到下面的结果:

③ 此时访问 order-service,因为 namespace

    不同,会导致找不到 userservice,控制台

    会报错:

 6. Nacos 和 Eureka 的区别

(1) Nacos 的服务实例分为两种 l 类型:

临时实例:如果实例宕机超过一定时间,

               会从服务列表剔除,默认的类型

非临时实例:如果实例宕机,不会从服务

                  列表剔除,也可以叫永久实例

配置一个服务实例为永久实例:

spring:cloud:nacos:discovery:ephemeral: false # 设置为非临时实例

(2) Nacos 和 Eureka 整体结构类似,服务注册、

    服务拉取、心跳等待,但是也存在一些差异:

(3) Nacos 与 eureka 的共同点

都支持服务注册服务拉取

都支持服务提供者心跳方式做健康检测

(4) Nacos 与 Eureka 的区别

Nacos 支持服务端主动检测提供者状态:

临时实例采用心跳模式

非临时实例采用主动检测模式

临时实例心跳不正常会被剔除,非临时

实例则不会被剔除

Nacos 支持服务列表变更的消息推送模

,服务列表更新更及时

Nacos 集群默认采用AP方式,当集群中

存在非临时实例时,采用CP模式

Eureka采用AP方式

七、Nacos 配置管理

1. 统一配置管理

当微服务部署的实例越来越多,达到数

十、数百时,逐个修改微服务配置就会

让人抓狂,而且很容易出错。我们需要

一种统一配置管理方案,可以集中管理

所有实例的配置

 

Nacos 一方面可以将配置集中管理,另一

方可以在配置变更时,及时通知微服务,

实现配置的热更新

(1) 在 nacos 中添加配置文件

然后在弹出的表单中,填写配置信息:

注意:项目的核心配置,需要热更新的配置

           才有放到 nacos 管理的必要。基本不

           会变更的一些配置还是保存在微服务

           本地比较好 

(2) 从微服务拉取配置

微服务要拉取 nacos 中管理的配置,并且与

本地的 application.yml 配置合并,才能完成

项目启动

① 引入 nacos-config 依赖

在 user-service 服务中,引入 nacos-config

的客户端依赖:

<!--nacos配置管理依赖-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

 ② 添加 bootstrap.yaml

在 user-service 中添加一个 bootstrap.yaml

文件,优先级高于application.yml,

内容如下:

spring:application:name: userservice # 服务名称profiles:active: dev #开发环境,这里是dev cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名

在 user-service 中将 pattern.dateformat 这个

属性注入到 UserController 中做测试:

@RestController
@RequestMapping("/user")
public class UserController {//注入nacos中的配置属性@Value("${pattern.dateformat}")private String dateformat;//编写controller,通过日期格式化器来格式化现在时间并返回@GetMapping("now")public String now(){return LocalDate.now().format(DateTimeFormatter.ofPattern(dateformat, Locale.CHINA));}
}

2. 配置热更新

最终目的,是修改 nacos 中的配置后

微服务中无需重启即可让配置生效

也就是配置热更新

要实现配置热更新,可以使用两种方式:

(1) 方式一

在 @Value 注入的变量所在类上添加注解

@RefreshScope

(2) 方式二

使用 @ConfigurationProperties 注解代替

@Value 注解

在 user-service 服务中,添加一个类,读

patterrn.dateformat 属性:

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
​
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {private String dateformat;
}

在 UserController 中使用这个类代替 @Value: 

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
​@Autowiredprivate UserService userService;
​@Autowiredprivate PatternProperties patternProperties;
​@GetMapping("now")public String now(){return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));}
}

注意: 不是所有的配置都适合放到配置中

           心,维护起来比较麻烦 建议将一些

           关键参数,需要运行时调整的参数

           放到 nacos 配置中心,一般都是自

           定义配置

3. 配置共享

其实微服务启动时,会去 nacos 读取多个

配置文件,

[spring.application.name]-[spring.profiles.active].yaml

    例如:userservice-dev.yaml

[spring.application.name].yaml

    例如:userservice.yaml

而 [spring.application.name].yaml 不包含环

境,因此可以被多个环境共享

(1) 添加一个环境共享配置

在 nacos 中添加一个 userservice.yaml 文件:

(2) 在 uer-service 中读取共享配置

① 在 user-service 服务中,修改 PatternProperties

类,读取新添加的属性:

② 在 user-service 服务中,修改 UserController,

添加一个方法:

(3) 运行两个 UserApplication,使用不同

    profile

修改 UserApplication2 这个启动项,改变其

profile 值:

UserApplication(8081) 使用的 profile 是

dev,UserApplication2(8082) 使用的

profile 是 test

启动 UserApplication 和 UserApplication2,

访问 http://localhost:8081/user/prop,

结果:

显然 dev 和 test 两个环境,都读取到了

envSharedValue 这个属性的值

(4) 配置共享的优先级

① 多环境配置共享

当 nacos、服务本地同时出现相同属性时,优先

级有高低之分:

[服务名]-[环境].yaml > [服务名].yaml > 本地配置

② 多服务配置共享

环境配置 >服务名.yaml > extension-config >

extension-configs > shared-configs > 本地配置

不同微服务之间可以共享配置文件,通过下面的

两种方式来指定:

方式一:

spring:application:name: userservice # 服务名称profiles:active: dev # 环境,cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 多微服务间共享的配置列表- dataId: common.yaml # 要共享的配置文件id

方式二:

spring:application:name: userservice # 服务名称profiles:active: dev # 环境,cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名extends-configs: # 多微服务间共享的配置列表- dataId: extend.yaml # 要共享的配置文件id

4.  搭建 Nacos 集群

(1) 集群结构图

(2) 搭建集群的基本步骤

① 搭建数据库,初始化数据库表结构

② 下载 nacos 安装包

③ 配置 nacos

④ 启动 nacos 集群

⑤ nginx 反向代理

1) 配置 nacos:

 ① 将配置文件重命名为 cluster.conf,添加内容

127.0.0.1.8845

127.0.0.1.8846

127.0.0.1.8847

② 打开注释,修改账号密码

 ③ 将 nacos 文件夹复制三份,分别命名

     为:nacos1、nacos2、nacos3

     然后分别修改三个文件夹中的

     application.properties,

④ 分别启动三个 nacos 节点:startup.cmd 

2) nginx 反向代理

① 修改conf/nginx.conf文件,配置如下:

② 启动 start nginx.exe

③ 在浏览器访问:http://localhost/nacos

④ 修改 java 代码中的 nacos 地址

3) 优化

① 实际部署时,需要给做反向代理的 nginx

    服务器设置一个域名,这样后续如果有服

    务器迁移 nacos 的客户端也无需更改配置.

② Nacos 的各个节点应该部署到多个不同服

    务器,做好容灾和隔离

 

八、Feign 远程调用

以前利用 RestTemplate 发起远程调用的代码:

 缺点:

① 代码可读性差,编程体验不统一

② 参数复杂 URL 难以维护

Feign 是一个声明式的 http 客户端,官方地址:

https://github.com/OpenFeign/feign

其作用就是帮助我们优雅的实现 http 请求的发

送,解决上面提到的问题

1. Feign 替代 RestTemplate

(1) 引入依赖

在 order-service 服务的 pom 文件中引入 Feign

的依赖:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

(2) 添加注解

在 order-service 的启动类添加注解开启 Feign

的功能:

(3) 编写 Feign 的客户端

在 order-service 中新建一个接口,内容如下:

@FeignClient("userservice")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}

这个客户端主要是基于 SpringMVC 的注解来

声明远程调用的信息,比如:

服务名称:userservice

请求方式:GET

请求路径:/user/{id}

请求参数:Long id

返回值类型:User

这样,Feign 就可以帮助我们发送 http 请求,

无需自己使用 RestTemplate 来发送了

(4) 测试

修改 order-service 中的 OrderService 类中的

queryOrderById 方法,使用 Feign 客户端代

替 RestTemplate:

 

(5) 总结

使用 Feign 的步骤:

① 引入依赖

② 添加 @EnableFeignClients 注解

③ 编写 FeignClient 接口

④ 使用 FeignClient 中定义的方法代替

    RestTemplate

2. 自定义配置

配置 Feign 日志有两种方式:

(1) 配置文件方式

① 局部生效

feign:  client:config: userservice: # 针对某个微服务的配置loggerLevel: FULL #  日志级别 

② 全局生效

feign:  client:config: default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置loggerLevel: FULL #  日志级别 

日志的级别分为四种: 

NONE不记录任何日志信息,这是默认值

BASIC:仅记录请求的方法,URL以及响应

              状态码和执行时间

HEADERS:在BASIC的基础上,额外记录

                     了请求和响应的头信息

FULL:记录所有请求和响应的明细,包括头

            信息、请求体、元数据

(2) Java 代码方式

需要先声明一个Bean:

public class DefaultFeignConfiguration  {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志级别为BASIC}
}

如果要全局生效,将其放到启动类的

@EnableFeignClients 这个注解中:

@EnableFeignClients(defaultConfiguration =

                DefaultFeignConfiguration .class) 
如果是局部生效,则把它放到对应的

@FeignClient这个注解中:

@FeignClient(value = "userservice", configuration

                = DefaultFeignConfiguration .class) 

 

3. Feign 使用优化

Feign 底层发起 http 请求,依赖于其它的框架

其底层客户端实现包括:

URLConnection默认实现,不支持连接池

Apache HttpClient :支持连接池

OKHttp:支持连接池

因此提高 Feign 的性能主要手段就是

① 使用连接池代替默认的 URLConnection

② 日志级别,最好用 basic none

Feign 添加 HttpClient 的支持:

(1)引入依赖

在 order-service 的 pom 文件中引入 Apache

的 HttpClient 依赖:

<!--httpClient的依赖 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>

(2)配置连接池

在 order-service 的 application.yml 中添加配置:

feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数

4. 最佳实践

Feign 客户端:

UserController: 

 

 代码一样,优化方法有两种:

(1) 继承

给消费者的 FeignClient 和提供者的

controller 定义统一的父接口作为标准

缺陷: 

① 不推荐这种方法,服务端和客户端共用

    接口,会出现服务紧耦合

② 父接口参数列表中的映射不会被继承

    因此 Controller 中必须再次声明方法、

    参数列表、注解

(2) 抽取

将 FeignClient 抽取为独立模块,并且把

接口有关的 POJO、默认的 Feign 配置都

放到这个模块中,提供给所有消费者使用

 基于抽取的最佳实践实现方式:

1) 创建一个 module,命名为 feign-api

    然后引入 feign 的 starter 依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2) 将 order-service 中编写的 UserClient

     UserDefaultFeignConfiguration

     制到 feign-api 项目中

 

删除 order-service 中的 UserClient、User、

DefaultFeignConfiguration 等类或接口

3) 在 order-service 中引入 feign-api 的依赖

<dependency><groupId>cn.itcast.demo</groupId><artifactId>feign-api</artifactId><version>1.0</version>
</dependency>

4) 修改 order-service 中的所有与上述三个组

    件有关的 import 部分,改成导入 feign-api

    中的包

5) 重启测试

报错是因为 UserClient 现在在 cn.itcast.feign.clients

包下,而 order-service 的 @EnableFeignClients

注解是在 cn.itcast.order 包下,不在同一个包,

无法扫描到 UserClient

6) 解决扫描包问题

方式一:

指定 Feign 应该扫描的包:

@EnableFeignClients(basePackages = "cn.itcast.feign.clients")

方式二:

指定需要加载的 Client 接口:(精准指定)

@EnableFeignClients(clients = {UserClient.class})

九、Gateway 服务网关

1. 网关的功能特性

① 对用户请求做身份认证权限校验

② 将用户请求路由到微服务,并实现

     负载均衡

③ 对用户请求做限流

 

在SpringCloud中网关的实现包括两种:

gateway

zuul

① Zuul 是基于 Servlet 的实现,属于阻塞

    式编程

② SpringCloudGateway 则是基于 Spring5

    中提供的 WebFlux,属于响应式编程

    实现,具备更好的性能。

2. 搭建网关服务基本步骤

(1) 创建 SpringBoot 工程 gateway,引入网关

     依赖和 nacos 服务发现依赖

<!--网关依赖-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency><!--nacos服务发现依赖-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

(2) 编写启动类

@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}

(3) 编写基础配置和路由规则

创建 application.yml 文件,内容如下:

server:port: 10010 # 网关端口
spring:application:name: gateway # 服务名称cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 网关路由配置- id: user-service # 路由id,自定义,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称predicates: # 路由断言,也就是判断请求是否符合路由规则的条件- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求

(4) 启动网关服务进行测试

重启网关,访问 http://localhost:10010/user/1 

时,符合 /user/** 规则,请求转发到 uri:

http://userservice/user/1,得到了结果:

 

(5) 网关路由的流程图

3. 路由断言工厂 (Route Predicate Factory)

路由配置包括:

路由 id:路由的唯一标示

路由目标 (uri):路由的目标地址,http

                        代表固定地址,lb 代表

                        根据服务名负载均衡

路由断言 (predicates):判断请求是否符合

                要求,符合则转发到路由目的地

路由过滤器 (filters):对请求或响应做处理

在配置文件中写的断言规则只是字符串

这些字符串会被 Predicate Factory 读取

并处理,转变为路由的判断条件

例如 Path=/user/** 是按照路径匹配,这个

规则是由 org.springframework.cloud.gateway.

handler.predicate.PathRoutePredicateFactory

类来处理的

像这样的断言工厂在 SpringCloudGateway

还有十几个

4. 过滤工厂 (GatewayFilter)

GatewayFilter 是网关中提供的一种过滤器,

可以对进入网关的请求和微服务返回的响应

做处理:

(1) 路由过滤器的种类

Spring 提供了 31 种不同的路由过滤器工厂。

例如:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

(2) 请求头过滤器

例如:给所有进入 userservice 的请求添加一

          个请求头:

          Truth=itcast is freaking awesome!

只需要修改 gateway 服务的 application.yml

文件,添加路由过滤即可:

spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # 过滤器- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头

当前过滤器写在 userservice 路由下,因此仅

仅对访问 userservice 的请求有效

(3) 默认过滤器

如果要对所有的路由都生效,则可以将过滤器

工厂写到 default 下。格式如下:

spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/**default-filters: # 默认过滤项- AddRequestHeader=Truth, Itcast is freaking awesome! 

5. 全局过滤器 (GlobalFilter)

(1) 全局过滤器的作用

全局过滤器与过滤器工厂都是处理一切进入

网关的请求和微服务响应

区别:

GatewayFilter 通过配置定义,处理逻辑是

固定的;

GlobalFilter 的逻辑需要自己写代码实现

定义方式是实现 GlobalFilter 接口

public interface GlobalFilter {/***  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理** @param exchange 请求上下文,里面可以获取Request、Response等信息* @param chain 用来把请求委托给下一个过滤器 * @return {@code Mono<Void>} 返回标示当前过滤器业务结束*/Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

(2) 自定义全局过滤器

1) 需求:定义全局过滤器,拦截请求,判断

              请求的参数是否满足下面条件:

① 参数中是否有 authorization,

② authorization 参数值是否为 admin

如果同时满足则放行,否则拦截

2) 实现:

自定义类,实现GlobalFilter接口,添加

@Order 注解:

@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();// 2.获取authorization参数String auth = params.getFirst("authorization");// 3.校验if ("admin".equals(auth)) {// 放行return chain.filter(exchange);}// 4.拦截// 4.1.禁止访问,设置状态码exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 4.2.结束处理return exchange.getResponse().setComplete();}
}

(3) 过滤器执行顺序

请求进入网关会碰到三类过滤器:

当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将三个合并到一个过滤器链

(集合) 中,排序后依次执行每个过滤器:

① 每一个过滤器都必须指定一个 int 类型的

    order 值,order值越小,优先级越高,执

    行顺序越靠前

② GlobalFilter 通过实现 Ordered 接口,或

    者添加@Order注解来指定 order 值,由

    我们自己指定

③ 路由过滤器和 defaultFilter 的 order 由

    Spring 指定,默认是按照声明顺序从 1

    递增

④ 当过滤器的 order 值一样时,会按照

    defaultFilter > 路由过滤器 > GlobalFilter

    的顺序执行

查看源码:

org.springframework.cloud.gateway.route.

RouteDefinitionRouteLocator#getFilters()

方法是先加载 defaultFilters,然后再加载

某个 route 的 filters,然后合并

org.springframework.cloud.gateway.handler.

FilteringWebHandler#handle() 方法会加载

全局过滤器,与前面的过滤器合并后根据

order 排序,组织过滤器链

6. 跨域问题

1. 定义

跨域:域名不一致就是跨域,主要包括:

域名不同: www.taobao.com 和 www.taobao.org

                  和 www.jd.com 和 miaosha.jd.com

域名相同,端口不同:localhost:8080 和

                                    localhost:8081

跨域问题:浏览器禁止请求的发起者与服务端

                 发生跨域 ajax 请求,请求被浏览器

                 拦截的问题

解决方案:CORS

2. 解决方式

在 gateway 服务的 application.yml 文件中,

添加下面的配置:

spring:cloud:gateway:# 。。。globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求 - "http://localhost:8090"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/7593.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS样式表

CSS样式表 1、CSS介绍 CSS Cascading Style Sheet 层叠样式表&#xff0c;或&#xff0c;级联样式表 表现HTML文件样式的计算机语言 修饰静态页面 配置脚本语言动态对网页元素进行样式格式化 排序 对元素的位置进行像素级精确控制 支持所有字体字号样式 对网页对象和模型样式…

springboot集成camunda

1、相关软件下载Camunda流程引擎快速入门——Hello World示例 2、由于camunda-modeler最新版本为5.12.0.界面不太一样。 可以安装历史版本4.12.0camunda-bpm camunda-modeler等历史版本下载 3、汉化Camunda Modeler汉化添加简体中文和繁体中文支持 4、集成如何实现Springbootca…

LVS负载均衡集群

目录 1.LVS 2.集群分类 3.负载均衡集群工作模式 4.负载均衡集群架构 5.LVS作用 6.LVS与nginx比较 7.ipvsadm工具 8.实验 第一台服务器 进入第二台第三台服务器下载http服务nfs服务rpcbind服务并启动 进入第四台服务器 9.总结 1.LVS 负载均衡的结构 2.集群分类 负载均衡…

Java基础---String str=new String(“tang“)创建了几个对象

目录 典型回答 常量池基本概念 字符串常量池的结构 再看字面量和运行时常量池 intern 还是创建了几个对象 intern的正确用法 典型回答 创建的对象数应该是1个或者2个如果常量池中存在&#xff0c;则直接new一个对象如果常量池不存在&#xff0c;则在常量池中创建一个对象…

MySQL数据库——MHA高可用

MySQL数据库——MHA高可用 一、MHA概述1&#xff0e;什么是 MHA2&#xff0e;MHA 的组成3&#xff0e;MHA 的特点 二、搭建 MySQL MHA1.Master、Slave1、Slave2 节点上安装 mysql5.72&#xff0e;修改 Master、Slave1、Slave2 节点的主机名&#xff0c;添加主从mysql的映射关系…

select……for update 到底加的什么锁

先上结论 主键索引唯一索引普通索引普通字段等值查询行锁行锁行锁间隙锁&#xff0c;锁表范围查询间隙锁&#xff0c;锁范围行间隙锁&#xff0c;锁范围行间隙锁&#xff0c;锁范围行间隙锁&#xff0c;锁表 数据表准备 DROP TABLE IF EXISTS t_user_test; CREATE TABLE t_u…

七牛云下载文件(显示在浏览器上)

最近在做关于如何将七牛云的文件下载下来&#xff0c;且在浏览器页面展示下载文件。 首先&#xff0c;我们需要注册七牛云账号 七牛云官网。 选择个人账户即可&#xff0c;若是需要企业账户&#xff0c;则可以选择企业账户。 注册成功绑定邮箱后&#xff0c;我们可以创建存储…

Linux——统信UOS(v20-1060a)部署.net core项目

部署.net core之前&#xff0c;请先确定已经安装了运行环境 Linux安装.net core环境 新建一个用于测试的 .net core web 项目 直接发布到文件夹&#xff0c;目标运行时选择 linux-64&#xff0c;这里根据你自己的操作系统的运行环境选择。 先点击完成&#xff0c;后面再设置参…

2023年Web安全学习路线总结!430页Web安全学习笔记(附PDF)

关键词&#xff1a;网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线、web安全攻防笔记、渗透测试路线图 网络安全的范畴很大&#xff0c;相较于二进制安全等方向的高门槛、高要求&#xff0c;Web安全体系比较成熟&#xff0c;在现阶段来看&#xff0c;但凡有自己…

【全文搜索选型】全文搜索 PostgreSQL 或 ElasticSearch

在本文中&#xff0c;我记录了在 PostgreSQL&#xff08;使用 Django ORM&#xff09;和 ElasticSearch 中实现全文搜索 (FTS) 时的一些发现。 作为一名 Django 开发人员&#xff0c;我开始寻找可用的选项来在大约一百万行的标准大小上执行全文搜索。有两个值得尝试的选项&…

怎样优雅地增删查改(一):从0开始搭建Volo.Abp项目

软件系统中数据库或者持久层的基本操作功能可以用Curd描述&#xff0c;Curd即 增加&#xff08;Create&#xff09;、更新&#xff08;Update&#xff09;、读取查询&#xff08;Retrieve&#xff09;和删除&#xff08;Delete&#xff09;&#xff0c; 这4个单词的首字母。 在…

【HTTP 协议2】如何构造 HTTP 请求

文章目录 前言一、地址栏输入二、HTML 特殊标签三、form 表单四、ajax总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 &#x1f4d7; Java数据结…