SpringCloud

SpringCloud
潘SpringCloud
SpringCloud简介
- Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
- SpringCloud的组件相当繁杂,拥有诸多子项目。重点关注Netflix,Neflix包含了四大组件:
- Eureka :服务注册中心,用于实现服务的注册和发现
- Ribbon:客户端负载均衡器,主要提供客户侧的负载均衡算法
- Hystrix:断路器,保护系统,控制故障范围
- Zuul:API网关,提供路由、负载均衡等多个作用
- 以及另一个子项目 Config:配置中心,实现配置统一管理
SpringCloud基本概念:集群和分布式
- 集群:
- 假设现在有一家小饭店,厨房只有一个厨师,他需要干买菜、洗菜、切菜、炒菜所有工作
- 当饭店生意火爆时,一个厨师忙不过来,于是老板再请了一个一样的厨师,两个厨师能炒一样的菜,这两个厨师的关系是集群
- 应用服务器集群部署
- 分布式:
- 阶段一:单应用架构
- 系统的初级都是应用和数据库都放在一台服务器上。
- 阶段二:应用服务器和数据库服务器分离
- 阶段三:应用服务器集群
- 随着访问量和流量的增加,假设数据库没有遇到瓶颈,对应用服务器集群来对请求进行分流,提高程序的性能。
- 阶段四:数据库压力变大-数据库读写分离
- 读写分离的话,这样以后的请求,查询的请求就可以去从库里面读数据,写的数据可以到主库中了
- 阶段五:使用搜索引擎、引入缓存机制缓解数据库的压力
- 阶段六:数据库的水平/垂直拆分
- 阶段七:应用的拆分
- 微服务架构(Microservice Architecture)是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。
- 在微服务架构系统中,围绕业务领域组件来创建应用,这些应用可独立地进行开发、管理和迭代。在分散的组件中使用云架构和平台式部署、管理和服务功能,使产品交付变得更加简单。
Spring Cloud和dubbo对比
- dubbo由于是二进制的传输,占用带宽会更少
- springCloud是http协议传输,带宽占用会比较多,同时使用http协议一般会使用JSON报文,消耗会更大
- dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决
- springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级
- dubbo的注册中心可以选择zk,redis等多种,springcloud的注册中心只能用eureka或者自研
Eureka 服务注册中心
- 作用:实现服务治理(服务注册与发现)
- 简介:Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。由两个组件组成:Eureka服务端和Eureka客户端。
- Eureka服务端用作服务注册中心。支持集群部署。
- Eureka客户端是一个java客户端,用来处理服务注册与发现。
- 在应用启动时,Eureka客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地。客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。
创建服务注册中心的步骤
- 创建一个maven主工程,命名为spring-cloud
- 创建一个Module子工程,该工程作为服务注册中心,命名为Eureka-server
- 创建一个服务提供者,即一个Module子工程,命名为service-hello,在配置文件中,将该服务注册在Eureka-server中
- 测试服务的多端口同时启动,观察Eureka-server中的信息
- 了解高可用性的服务注册中心。
Maven主工程创建
- 创建一个Maven主工程,命名为Spring-Cloud,在其pom文件引入依赖,spring Boot版本为2.1.6.RELEASE,Spring Cloud版本为Finchley.RELEASE。这个pom文件作为父pom文件,起到依赖版本控制的作用,其他module工程继承该pom
org.springframework.boot spring-boot-starter-parent 2.1.6.RELEASE com.nothing springcloud 0.0.1-SNAPSHOT springcloud Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 2.1.6.RELEASE com.nothing springcloud 0.0.1-SNAPSHOT springcloud Demo project for Spring Boot - 主工程pom文件中添加spring-boot-starter和spring-boot-starter-test两个依赖包,添加一个spring-boot-maven-plugin的插件,添加一个依赖版本管理器。
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin
搭建服务注册中心子项目Eureka-server
右键工程->创建model-> 选择spring initialir
点击next ,Group填写包名 artifact填写项目名,命名为eureka-server
点击next,选中spring cloud discovery,在中间选项中勾选上Eureka server
在pom文件中,继承主工程pom文件,添加spring-cloud-starter-netflix-eureka-server依赖包。
com.nothing springcloud 0.0.1-SNAPSHOT org.springframework.cloud spring-cloud-starter-netflix-eureka-server 项目创建好后,结构如下,其中EurekaServiceApplication为springboot启动类,在该启动类中添加一个注解@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);
}
}项目配置文件采用yml的配置语法:
- 其中spring.application.name是唯一的,表明自己的服务名
- Server.port是唯一的,表示启动该项目所用的tomcat端口
- Eureka.instance.hostname表明该注册中心的ip地址
#端口名
server:
port: 8761
eureka:
instance:
hostname: localhost
#表明自己是一个server
client:
registerWithEureka: false #表明当前项目只作为服务器,不作为客户端
fetchRegistry: false #只注册自身
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:name: eurka-server
- 完成上面所有配置后,启动EurekaServerApplication启动类,在浏览器中访问http://localhost:8761/其中Application处是所有注册在注册中心的服务列表,当前没有任何服务注册
General info是key-value格式的当前注册中心的相关信息
Instance info是当前所有在运行的注册中心实例的信息开发一个微服务
创建一个子项目Module,命名为service-hello,创建过程和eureka-server类似,但不需要勾选eureka-server选项。
pom文件继承主工程pom文件,并添加两个依赖:spring-cloud-starter-netflix-eureka-client和spring-boot-starter-web
com.nothing springcloud 0.0.1-SNAPSHOT org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web 配置文件如下:
Server.port指明当前项目的端口为8005
Spring.application.name指明当前服务的名称,服务与服务间的调用,后面的客户端调用服务,都是根据该名称
Eureka.client.serviceUrl.defaultZone指明该服务注册中心地址,即是我们刚刚部署启动的eureka-server
server:
port: 8762
#服务的name,服务与服务之间互相调用都是根据该name
spring:
application:name: service-hello
#指明服务注册中心的地址
eureka:
client:serviceUrl: defaultZone: http://localhost:8761/eureka/
在spring-boot启动类中,加上一个注解@EnableEurekaClient,表明自己是一个服务提供方。
在启动类的子目录或同级目录下,编写HelloController,代码如下:
@EnableEurekaClient
@SpringBootApplication
public class ServiceHelloApplication {public static void main(String[] args) { SpringApplication.run(ServiceHelloApplication.class, args); }
}
启动service-hello,访问服务注册中心地址: http://localhost:8761/ 可以看到我们的service-hello微服务已经注册在eureka-server中,这时可以直接访问微服务的地址端口,并附带参数gcm http://localhost:8005/hello?name=gcm 可以看到浏览器打印出控制器返回的信息
Ribbon简介
- 在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。
- Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
- ribbon是一个客户端负载均衡器,可以很好的控制http和tcp的一些行为。Feign默认集成了ribbon。
Ribbon与负载均衡
- 负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。
- Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
- Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的
restTemplate详解
- restTemplate可以使用REST风格的http请求去调用服务注册中心的微服务;
其中主要的方法有: - GET请求:
- getForEntity函数
- 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
- getForObject函数
- 发送一个HTTP GET请求,返回的请求体将映射为一个对象
- POST请求:
- postForEntity() 函数
- POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的
- postForObject() 函数
- POST 数据到一个URL,返回根据响应体匹配形成的对象
- DELETE请求:
- delete()函数
- 在特定的URL上对资源执行HTTP DELETE操作
- PUT请求:
- put()函数
- PUT 资源到特定的URL
- 其他请求:
- postForLocation()函数
- POST 数据到一个URL,返回新创建资源的URL
- execute()函数
- 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象
- exchange() 函数
- 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中 映射得到的
什么是 feign
- Feign 是一个声明式的 Web Service 客户端。它的出现使开发 Web Service 客户端变得很简单。使用 Feign 只需要创建一个接口加上对应的注解,比如:@FeignClient 注解。 Feign 有可插拔的注解,包括 Feign 注解和 AX-RS 注解。Feign 也支持编码器和解码器,Spring Cloud Open Feign 对 Feign 进行增强支持 Spring Mvc 注解,可以像 Spring Web 一样使用HttpMessageConverters 等。
- Feign 也是一种声明式、模板化的 HTTP 客户端。在 Spring Cloud 中使用 Feign,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。
Feign的特性
- 可插拔的注解支持,包括 Feign 注解和AX-RS注解。
- 支持可插拔的 HTTP 编码器和解码器。
- 支持 Hystrix 和它的 Fallback。
- 支持 Ribbon 的负载均衡
- 支持 HTTP 请求和响应的压缩。
- 整合了 Ribbon 和 Hystrix,从而不需要开发者针对 Feign 对其进行整合。Feign 还提供了 HTTP 请求的模板,通过编写简单的接口和注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。
@FeignClient注解的一些属性
- Value:要调用的服务名称
- url:url 一般用于调试,可以手动指定 @FeignClient 调用的地址。
- decode404:当发生404错误时,如果该字段为 true,会调用 decoder 进行解码,否则抛出 FeignException。
- configuration:Feign 配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。
- fallback:熔断机制,调用失败时,走的一些回退方法,可以用来抛出异常或给出默认返回数据,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,- fallback 指定的类必须实现 @FeignClient 标记的接口。
- fallbackFactory:工厂类,用于生成 fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
- path:定义当前 FeignClient 的统一前缀,自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping
Feign原理简述
- 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。
- RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
- RequestTemplate生产Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
- 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
RestTemplate + Ribbon与Feign对比
- RestTemplate + Ribbon与Feign有很多相同的地方,但也有各自的差异,对比一下差异如下:
- 角度 RestTemplate + Ribbon Feign(自带Ribbon)
- 可读性、可维护性 欠佳(无法从URL直观了解这个远程调用是干什么的) 极佳(能在接口上写注解,方法名称也是可读的,能一眼看出这个远程调用是干什么的)
- 开发体验 欠佳(拼凑URL不性福) 极佳(写出漂亮的代码,女朋友更爱你了)
- 风格一致性 欠佳(本地API调用和RestTemplate调用的代码风格截然不同) 极佳(完全一致,不点开Feign的接口,根本不会察觉这是一个远程调用而非本地API调用)
- 性能 较好 中等(性能是RestTemplate的50%左右;如果为Feign配置连接池,性能可提升15%左右)
- 灵活性 极佳 中等(内置功能能满足大多数项目的需求)
Feign的相关属性配置
- 目前,feign不支持GET请求直接传递POJO对象的,目前解决方法如下:
- 把POJO拆散成一个一个单独的属性放在方法参数中
- 把方法参数组装成Map传递
- 使用GET传递@RequestBody,但此方式违反restful风格
- Spring Cloud Feign支持对请求和响应进行GZIP压缩,以提高通信效率。
由于开启GZIP压缩之后,Feign之间的调用数据通过二进制协议进行传输,返回值需要修改为ResponseEntity<byte[]>才可以正常显示,否则会导致服务之间的调用乱码。 - 作用在所有Feign Client上的两种配置方式:
- 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
- 当服务I由于某种原因无法响应时,用户请求就会卡在服务 I 的远程调用上,假如超时失败时间设置为2秒,那么在这2秒内容器的当前线程就会一直被堵塞在该调用中。 我们假设容器最大线程数为100,如果此时又有另外99个请求都需要调用服务 I, 那么这99个线程同样会被堵塞,这样就会因为容器线程耗尽而导致该应用无法响应其它任何请求。因为一个服务挂掉而导致整个应用不可用显然是无法接受的。
Spring Cloud中的Hystrix
- 为了应对服务雪崩,一种常见的做法是手动服务降级。而Hystrix的出现,给我们提供了另一种选择。我们可以通过使用Hystrix来应对服务异常。
- Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。
- 较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
- 断路打开后,可以避免连锁故障,fallback方法可以直接返回一个固定值。
Spring Cloud中的Hystrix实现断路器的原理
- 资源隔离:
- 首先,Hystrix对每一个依赖服务都配置了一个线程池,对依赖服务的调用会在线程池中执行。例如,我们设计服务I的线程池大小为20,那么Hystrix会最多允许有20个容器线程调用服务I,如果超出20,Hystrix会拒绝并快速失败。这样即使服务I长时间未响应,容器最多也只能堵塞20个线程,剩余80个线程仍然可以处理用户请求。
- 快速失败:
- 快速失败是防止资源耗尽的关键一点。当Hystrix发现在过去某段时间内对服务I的调用出错率达到某个阀值时,Hystrix就会“熔断”该服务,后续任何向服务I的请求都会快速失败,而不是白白让调用线程去等待。
- 自我修复:
- Zuul是Netflix 公司开源的一个 API网关组件,提供了认证、鉴权、限流、动态路由、监控、 弹性、安全、负载均衡、协助单点压测、静态响应等边缘服务的框架 。
- Zuul 处理每个请求的方式是针对每个请求使用一个线程来处理 通常情况下,为了提高性能, 所有请求会被放到处理队列中,从线程池中选取空闲线程来处理该请求。2016 年年底, Netflix将它们的网关服务zuul 进行了升级,全新的zuul2将HTTP 请求的处理方式从同步变成了异步,以 提升其处理性能
- Zuul是Netflix出品的一个基于JVM 路由和服务端的负载均衡器
为什么需要Spring Cloud Zuul
- Zuul和Ribbon以及Eureka相结合,可以实现智能路由和负载均衡的功能,可以将流量按照某种策略分发到集群中的多个实例。
- 统一了对外暴露接口,外界系统不需要知道微服务系统中各服务之间调用的复杂性,也保护了内部微服务的api接口。
- 可以统一做用户身份认证,权限验证,这样就不用在每个微服务中进行认证。
- 可以统一实现监控、日志的输出。
- 客户端请求多个微服务时,可以只请求Zuul一次,在Zuul中请求多个微服务,减少客户端和微服务的交互次数。
路由网关(zuul)基本功能
- Zuul的基本功能如如下:
- Authentication:认证
- Insights:洞察
- Stress Testing:压力测试
- Canary Testing:金丝雀测试
- Dynamic Routing:动态路由
- Service Migration:服务迁移
- Load Shedding:负载脱落
- Security:安全
- Static Response handling:静态响应处理
- Active/Active traffic management:主动/主动流量管理
功能概括
- 简要概括如下:
- 请求进来的时候先去匹配path,然后用这个path的路由名去找对应的服务名,这里需要手动去命名服务名称,配合ribbon.listOfServers参数实现服务与实例的维护。由于存在多个实例,API网关在进行路由转发时需实现负载均衡,于是使用Ribbon的配合,Zuul中自带了对Ribbon的依赖。
- 参数说明:
- ribbon.eureka.enabled:由于zuul.routes.<路由名>.serviceId用来指定服务名称,默认Ribbon会根据发现机制来获取配置服务名对应的实例清单,但是,这里没有使用Eureka之类的服务发现治理框架,所以需要将该参数设为false,否则配置的serviceId获取不到对应实例的清单
- eureka-service.ribbon.listOfServers:该参数内容与zuul.routes.<路由名>的配置相对应,此配置打头的eureka-service对应了serviceId的值,相当于手动维护了在该应用内部手工维护了服务与实例的对应关系
路由分析
- 传统路由配置
- 传统路由就是不依赖于服务发现机制通过配置文件映射服务实例关系来实现的API网关对外请求路由。
- 面向服务路由
- 当有外部请求到达API网关的时候,根据请求的URL路径去匹配path的规则,通过path找到路由名,去找对应的serviceId的服务名
- 传统路由就会去根据这个服务名去找listOfServers参数,从而进行负载均衡和请求转发
- 面向服务路由会从注册到服务治理框架中取出服务实例清单,通过清单直接找到对应的实例地址清单,从而通过Ribbon进行负载均衡选取实例进行路由(请求转发)
SpringCloud动态路由的作用
- Spring Cloud 动态路由的作用是在微服务架构中实现灵活的路由管理和请求转发。
- 服务发现和负载均衡: 在微服务架构中,服务的数量可能非常庞大,并且会频繁地进行水平扩展或缩减。动态路由可以通过与服务注册中心结合,自动发现可用的服务实例,并提供负载均衡策略,确保请求能够均匀地分发到各个服务实例。
- 版本管理和灰度发布: 动态路由可以根据请求的特定条件(例如请求头、请求参数等)将请求导向不同的服务版本或者不同的处理逻辑。这样可以方便地进行版本管理和灰度发布,使得新版本的服务能够逐步接收流量,降低系统更新的风险。
- 动态配置和运维调整: 动态路由允许运维人员根据需要随时修改路由规则,而无需重启服务。这样可以方便地进行配置调整、故障切换、容错处理等运维操作。
- 微服务拆分和聚合: 动态路由可以根据请求的不同部分,将请求拆分成多个微服务的子请求,并将它们的结果聚合返回给客户端。这种方式可以帮助实现微服务的细粒度拆分和服务组合,提高系统的灵活性和性能。
请求过滤
- 微服务应用中的每个客户端在提供服务的接口时,都会将访问权限加以限制,并不会放开所有的接口,为了安全,我们应该为每个微服务加入校验签名和鉴权等的过滤器或者拦截器,这样一来,会增加日后系统的维护难度,同时大部分的校验和鉴权的逻辑代码是相同的,那么我们就应该将这些重复逻辑提取出来,上文中曾说到“Zuul相当于整个微服务系统的门面”,那么接下来我们来看下在zuul网关中实现客户端的请求校验,即Spring Cloud Zuul 的核心功能之一的请求过滤。
- 我们先定义一个简单的Zuul过滤器,实现检查HttpServletRequest中是否有accessToken参数,如果有就进行路由,没有则拒绝访问,还回 401 Unauthorized 错误
服务过滤—ZuulFilter
- 有以下四个方法:
- filterType:在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
- pre:在请求被路由之前调用
- route:路由请求时被调用
- post:在routing和error过滤器之后被调用
- error:处理请求时发生错误时被调用
- 路由映射主要通过pre类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则是由routing类型的过滤器来完成,对pre类型过滤器获得的路由地址进行转发。
- filterOrder:通过int值来定义过滤器的执行顺序,如果有多个同类型过滤器,则按照值决定过滤顺序,数值越小优先级越高。
- shouldFilter:返回一个boolean值来判断该过滤器是否要执行,我们可以通过此方法来指定过滤器的有效范围。
- run:过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
服务过滤—请求生命周期
外部http请求到达api网关服务的时候,首先它会进入第一个阶段pre,在这里它会被pre类型的过滤器进行处理。该类型过滤器的主要目的是在进行请求路由之前做一些前置加工,比如请求的校验等。
在完成了pre类型的过滤器处理之后,请求进入第二个阶段route,也就是之前说的路由请求转发阶段,请求将会被route类型的处理器处理。这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程,当服务实例请求结果都返回之后,route阶段完成。
请求进入第三个阶段post。此时请求将会被post类型的过滤器处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在post类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。
另外,还有一个特殊的阶段error,该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是post类型的过滤器,因为它需要通过post过滤器将最终结果返回给请求客户端(对于error过滤器的处理,在spring cloud zuul的过滤链中实际上有一些不同)
注意:同一个项目中,4种类型的过滤器可以同时存在,执行顺序是pre、routing、post、error
服务过滤
在代码中,我们通过判断参数token的值来决定是否拦截请求。
先得到RequestContext对象ctx,通过ctx对象得到HttpServletRequest对象request。
再通过request.getParameter(“token”);得到token的值,
如果token值为null,则通过ctx 的setSendZuulResponse(false);方法设置拦截请求,即不能
通过过滤,此时我们通过HttpServletResponse对象转发到错误页面error.html。需要在static下新建error.html
禁用过滤器
禁用过滤器,第一想到的是自定义的过滤器中shouldFilter返回false,实际应用中,这样还需要重新编译代码。
Zuul贴心地提供了一个参数用来禁用指定过滤器
zuul.<过滤器名>.<过滤器类型>.disable=true
如:前面我们写了一个MyFilter类,过滤类型为 pre
禁用此过滤器 pre不能改成其他类型,否则禁用失败,程序依然会过滤
zuul.MyFilter.pre.disable=true
Cookie与头信息
默认情况下,Zuul在请求路由时会过滤掉HTTP请求头信息中的一些敏感信息,防止这些敏感的头信息传递到下游外部服务器。但是如果我们使用安全框架如Spring Security、Apache Shiro等,需要使用Cookie做登录和鉴权,这时可以通过zuul.sensitiveHeaders参数定义,包括Cookie、Set-Cookie、Authorization三个属性来使Cookie可以被传递。
全局设置:zuul. sensitiveHeaders=Cookie,Set-Cookie,Authorization
指定路由设置:
在使用 Zuul 进行服务网关开发时,通常需要继承 ZuulFilter 类,并实现其中的一些方法来扩展或定制 Zuul 的功能。下面是四个最重要的方法:
shouldFilter(): 该方法决定是否启用当前的 filter,返回 true 表示启用,false 则表示禁用。
run(): 该方法是实际执行过滤逻辑的地方。如果 shouldFilter() 方法返回 true,则会执行 run() 方法中的代码逻辑。
filterType(): 该方法决定当前 filter 的类型,包括 “pre”、”route”、”post”、”error” 四种类型。分别代表“路由之前”、“路由之时”、“路由之后”、“错误处理”这四个阶段。
filterOrder(): 该方法决定了 filter 的执行顺序。数值越小的 filter 会先执行。
通过重写这些方法,可以实现自定义的服务过滤逻辑,例如校验请求参数、添加请求头、限流、鉴权等功能。同时,也可以根据业务需求灵活调整 filter 的执行顺序,确保所有的 filter 能够按照正确的顺序依次执行
ZuulFilter过滤器类型有哪些?
- PRE(路由之前): 这种过滤器在请求被路由之前执行。可用于实现身份验证、参数校验、请求日志记录等功能。
- ROUTE(路由之时): 这种过滤器将请求路由到服务实例。用于构建发送给服务的请求,例如添加头信息、转换请求体等。
- POST(路由之后): 这种过滤器在路由到服务之后执行。可用于处理服务的响应,例如添加响应头、处理响应结果等。
- ERROR(错误处理): 在其他阶段发生错误时执行该过滤器。可用于统一处理路由过程中的异常情况,例如记录错误信息、返回特定格式的错误响应等。
过滤器的执行顺序?
- 在 Zuul 中,过滤器的执行顺序由它们的 filterOrder() 方法返回的值来确定。数值越小的过滤器会先执行,而数值越大的过滤器会后执行。如果有多个过滤器在同一阶段,那么它们的执行顺序将根据它们的 filterOrder() 返回值的大小来确定。
- 通常情况下,开发者可以通过实现 filterOrder() 方法来指定过滤器的执行顺序,确保过滤器按照期望的顺序执行。例如,若希望某个过滤器在所有其他过滤器之前执行,则可以将它的 filterOrder() 返回一个较小的负数值。
为什么需要集中化配置
- 应用一般都会有配置文件,即便号称是“零配置”的 Spring Boot 应用,也无法完全做到不使 用配置文件 ,毕竟配置文件就是为了迎合软件的个性化需求 。一个带配置的应用程序,部署了多个 实例在若干台机器上,如果配置发生了变化,那么,就需要对该应用所有的实例进行配置的变更。 随着单块架构向微服务架构演进之后,微服务的应用数量也会剧增 。同时, 每个微服务都有自 己的配置文件,这些文件如果都散落在各自的应用中 ,必然会对应用的升级和配置管理带来挑战 毕竟谁也没有能力去手工配置那么多做服务的配置文件。 而且,对运维来说,一方面于工配置工作量很大,几乎不可能完成;另一方面, 相对而言,人为的操作会加大出错的几率。 所以,外部化 和中心化的配置中心, 变成了解决微服务配置问题的一个有力的途径。
配置的分类
- 按配置的来源划分
- 按配置的来源划分,主要有源代码、文件、数据库连接、远程调用等 。
- 按适用的环境划分
- 按配置的适用环境划分,可分为开发环境、测试环境、预发布环境、生产环境等。
- 按配置的集成阶段划分
- 按配置的集成阶段划分,可分为编译时、打包时和运行时。编译时,最常见的有两种,一是源代码级的配置, 二是把配置文件和源代码一起提交到代码仓库中。打包时,即在应用打包阶段通过 某种方式将配置(一般是文件形式)打入最终的应用包中。运行时,是指应用启动前并不知道具体 的配置,而是在启动时,先从本地或远程获取配置,然后再正常启动。
- 按配置的加载方式划分
- 创建符合要求的、易于使用的配置中心,至少需要满足以下几个核心需求。
- 面向可配置的编码。编码过程中,应及早考虑将后期可能经常变更的数据,设置为可以配置的配置项,从而避免在代码里面硬编码。
- 隔离性。不同部署环境下,应用之间的配置是相互隔离的,例如,非生产环境的配置不能用于 生产环境。
- 一致性。相同部署环境下的服务器应用配置应该具有一致性, 即同个应用 的所有的实例使用同一份配置。
- 集中化配置。在分布式环境下,应用配置应该具备可管理性,即提供远程管理配置的能力。
Spring Cloud Config
- 在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是服务器(config server),二是客户端( config client )。
- Config Server存储后端的默认实现使用了Git ,因此它可以轻松地支持标记版本的配置环境, 并且可以通过广泛的工具来访问管理内容。
搭建config-client项目时需要注意什么?
要新建一个bootstrap.properties,这是由spring boot的加载属性文件的优先级决定的,你想要在加载属性之前去spring cloud config server上取配置文件,那spring cloud config相关配置就是需要最先加载的,而bootstrap.properties的加载是先于application.properties的,所以config client要配置config的相关配置就只能写到bootstrap.properties里。
spring.application.name要和配置文件的名称一致 ,如spring.application.name=config-client
spring.cloud.config.profile对应开发环境 ,如spring.cloud.config.profile=dev
两者共同决定对应的配置文件,如以上配置对应config-client-dev.properties或
config-client-dev.yml
总结
- Config server配置中心服务端,是从仓库拉取配置文件到项目中,供其它微服务项目调用。仓库配置有远程仓库(git)和本地仓库两种,推荐使用远程仓库。两种仓库同时配置时,项目将会选择本地仓库配置,而舍弃远程仓库配置。
- Config Client是普通的微服务项目,从配置中心服务端拉取对应的配置文件。需要注意的是Config Client项目本身配置文件的加载顺序, bootstrap.properties的加载是先于application.properties的,所以config client要配置config的相关配置就只能写到bootstrap.properties里。
- 远程仓库如何配置?
- 创建远程仓库: 在选择的代码托管服务上创建一个新的仓库。这个仓库将用来存储项目的代码。
- 获取远程仓库地址: 在仓库创建成功后,获取仓库的远程地址。这个地址通常是一个URL,可以通过HTTPS或SSH协议来访问。
- 配置本地仓库的远程地址: 在本地项目的根目录下,使用Git命令将远程仓库地址与本地仓库关联起来。通常使用 git remote add 命令进行配置。
- 推送代码到远程仓库: 将本地仓库中的代码推送(push)到远程仓库中。这样可以确保远程仓库和本地仓库的代码同步。
- 如何直接通过url访问配置中心Config server从仓库加载到本地的配置文件?
- 确定配置文件的URL: 在配置中心中,每个配置文件都有一个唯一的URL。这个URL通常由配置中心的基本URL以及配置文件的路径组成。
- 构建访问URL: 将配置中心的基本URL与配置文件的路径组合起来,构建完整的配置文件URL。
- 通过HTTP请求获取配置文件: 使用HTTP客户端(如浏览器、curl命令等)向构建好的URL发送GET请求,以获取配置文件内容。
- 解析配置文件内容: 获取到配置文件后,可以将其内容解析为适当的格式,如Properties文件、YAML文件等,以便在应用程序中使用。