本文主要是介绍02-微服务架构集大成者-SpringCloud,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
微服务架构集大成者-SpringCloud
v1.2.03_201909_BoBo
课程目标:
- 服务注册与发现组件-SpringCloudNetflixEureka
- 服务网关组件-SpringCloudGateway
- 配置组件-SpringCloudConfig
- 服务总线组件-SpringCloudBus
1. SpringCloud概述
简介
Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, one-time tokens, global locks, leadership election, distributed sessions, cluster state). Coordination of distributed systems leads to boiler plate patterns, and using Spring Cloud developers can quickly stand up services and applications that implement those patterns. They will work well in any distributed environment, including the developer’s own laptop, bare metal data centres, and managed platforms such as Cloud Foundry.
2. SpringCloudNetflixEureka服务注册与发现
2.1 简介说明
Spring Cloud Netflix provides Netflix OSS integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms. With a few simple annotations you can quickly enable and configure the common patterns inside your application and build large distributed systems with battle-tested Netflix components. The patterns provided include Service Discovery (Eureka), Circuit Breaker (Hystrix), Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon)…
Spring Cloud Netflix通过自动配置和绑定到Spring环境和其他Spring编程模型习语,为Spring引导应用程序提供Netflix OSS集成。通过一些简单的注释,您可以在应用程序中快速启用和配置通用模式,并使用经过战斗测试的Netflix组件构建大型分布式系统。提供的模式包括服务发现(Eureka)、断路器(Hystrix)、智能路由(Zuul)和客户端负载平衡(Ribbon)
Spring Cloud Netflix功能:
服务发现:可以注册Eureka实例,客户机可以使用Spring管理的bean发现实例。
服务发现:可以用声明式Java配置创建嵌入式尤里卡服务器
断路器:Hystrix客户机可以用一个简单的注释驱动方法装饰器构建。
断路器:具有声明式Java配置的嵌入式Hythx仪表板
声明性REST客户机:feign创建一个用JAX-RS或SpringMVC注释修饰的接口的动态实现。
客户端负载平衡器:功能区
外部配置:从Spring环境到Archius的桥梁(使用Spring引导约定启用Netflix组件的本地配置)
路由器和过滤器:zuul过滤器的自动重新定位,以及反向创建代理的配置方法的简单约定
2.2 Eureka服务端-注册中心
(1)创建tensquare_eureka子模块
【了解】
单独新建微服务项目,也可。
(2)引入依赖
1)父工程pom.xml定义(锁定)SpringCloud版本
<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.SR2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
2)tensquare_eureka模块pom.xml引入eureka-server
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies>
(2)添加application.yml
server:#服务端口port: 8761
spring:application:## 应用名称,会在Eureka中作为服务的id标识(serviceId)name: tensquare-eureka
eureka:client:#是否将当前微服务注册到Eureka服务中。自己是注册中心,因此无需注册。register-with-eureka: false#是否从Eureka中获取注册信息。自己是注册中心,因此无需获取。fetch-registry: false#Eureka客户端与与Eureka服务端进行交互的地址Map表service-url:# EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。defaultZone: http://localhost:8761/eureka/
(3)编写启动类
创建包com.tensquare.eureka ,包下建立类
com.tensquare.eureka.EurekaApplication
package com.tensquare.eureka;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/*** Eureka服务的启动类*/@SpringBootApplication//开启Eureka服务@EnableEurekaServerpublic class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class);}}
(4)启动运行启动类,然后在浏览器地址栏输入 http://localhost:8761/
运行效果如下:
2.3 Eureka客户端服务注册
我们现在就将所有的微服务都注册到Eureka中,这样所有的微服务之间都可以互相调用
了。
(1)将其他微服务模块添加依赖,公共模块、父模块等除外。
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
(2)修改每个微服务的application.yml,添加注册eureka服务的配置
eureka:client:#Eureka服务的地址service-url:defaultZone: http://localhost:8761/eureka/instance:#用于表示在猜测主机名时,服务器的IP地址应该与操作系统报告的主机名相对应。(注册服务和客户端如果在一台机器上则无需配置)prefer-ip-address: true#强制只用客户端本机的哪个ip去注册,默认,本机所有ip#ip-address: 192.168.22.27
(3)修改每个服务类的启动类,添加注解@EnableEurekaClient
或@EnableDiscoveryClient
(推荐),都可以省略。
//开启Eureka客户端功能
//@EnableEurekaClient
@EnableDiscoveryClient
(4)启动测试:将每个微服务启动起来,会发现eureka的注册列表中可以看到这些微服务了(8个):
3. SpringCloudGateway服务网关
简介
This project provides a library for building an API Gateway on top of Spring MVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
这个项目提供了一个在SpringMVC之上构建API网关的库。SpringCloudGateway的目标是提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,如:安全性、监视/度量和弹性。
特性:
-
Spring Cloud网关功能:
-
基于Spring Framework 5、Project Reactor和Spring Boot 2.0构建
-
能够在任何请求属性上匹配路由。
-
谓词和过滤器是特定于路由的。
-
Hystrix断路器集成。
-
春云发现整合
-
易于编写谓词和筛选器
-
请求速率限制
-
路径重写
Zuul和Gateway的区别:
Zuul构建于 Servlet 2.5,兼容 3.x,使用的是阻塞式的 API,不支持长连接,比如 websockets。
Spring Cloud Gateway构建于 Spring 5+,基于 Spring Boot 2.x 响应式的、非阻塞式的 API。同时,它支持 websockets,和 Spring 框架紧密集成,开发体验相对来说十分不错。
另外的说明:
Spring Cloud Gateway是Spring官网基于Spring 5.0、 Spring Boot 2.0、Project Reactor等技术开发的网关服务。
Spring Cloud Gateway基于Filter链提供网关基本功能:安全、监控/埋点、限流等。
Spring Cloud Gateway为微服务架构提供简单、有效且统一的API路由管理方式。
Spring Cloud Gateway是替代Netflix Zuul的一套解决方案。
Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对
应的微服务。 Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从
而加强安全保护。Spring Cloud Gateway本身也是一个微服务,需要注册到Eureka服务注册中心。
网关的核心功能是:过滤和路由
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GFslOnR-1570001407212)(springcloud.assets/1566352414844.png)]
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都可经过网关,然后再由网关来实现 鉴权、动态路由等等操作。Gateway就是我们服务的统一入口。
核心概念
路由(route) 路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为
真,说明请求URL和配置路由匹配。
断言(Predicate) Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何
信息比如请求头和参数。
过滤器(Filter) 一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。
使用网关的好处
问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性。
- 存在跨域请求,在一定场景下处理相对复杂。
- 认证复杂,每个服务都需要独立认证。
- 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
- 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。
以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性,典型的架构图如图所示:
使用 API 网关后的优点如下:
- 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数。
需求
搭建一个网关,提供路由转发功能。这里转发基础微服务的请求。
路由配置路径转发
面向URL(http协议)的路由
(1)新建一个SpringBoot微服务工程tensquare_gateway
,将其注册到注册中心
(2)引入依赖,参考如下
pom.xml
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency></dependencies>
(3)resources中新建配置文件,并加入相关配置
application.yml
server:#网关的默认端口是80port: 80
spring:application:# 应用的名字name: tensquare-gatewaycloud:gateway:#配置路由规则routes:# 路由id,可以随意写- id: base-service# 代理的服务地址uri: http://localhost:9001# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务- Path=/label/**
说明:
默认端口为80(http)或443(https)
上述配置,将符合 Path 规则的一切请求,都代理到 uri 参数指定的地址
本例中,我们将路径中包含有 /label/** 开头的请求,代理到http://localhost:9001/label/**
注意:
Gateway和SpringMVC的依赖有冲突,不能引入spring-boot-starter-web
的起步依赖。
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.
(4)新建SpringBoot的启动类
com.tensquare.gateway.GatewayApplication
package com.tensquare.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}}
(5)启动微服务,通过浏览器访问下面的连接,会得到响应:
http://localhost/labelhttp://localhost/label/1
面向服务(lp协议)的路由
在刚才的路由规则中,把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然不合理。
应该根据服务的名称,去Eureka注册中心查找 服务对应的所有实例列表,然后进行动态路由!
将配置中心注册到Eureka中
(1)网关微服务中引入Eureka客户端的依赖,参考如下
pom.xml
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
(2)修改application.yml 文件如下:
server:port: 80
eureka:client:service-url:defaultZone: http://localhost:8761/eurekainstance:prefer-ip-address: true
spring:application:name: tensquare-gatewaycloud:gateway:routes:# 路由id,可以随意写- id: base-service# 代理的服务地址#uri: http://localhost:9001#代理的服务地址;lb表示从eureka中获取具体服务,后面的名字必须是Eureka中注册的服务的名字uri: lb://tensquare-base# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务- Path=/label/**
(3)启动类上添加Eureka客户端开启的注解(可省略)
com.tensquare.gateway.GatewayApplication
@EnableDiscoveryClient
启动测试:重启网关 ,发现仍然可以转发。
http://localhost/label
负载均衡:
Gateway使用面向服务lp协议的路由时,自动使用Ribbon进行负载均衡访问。
提示:
路由配置中uri所用的协议为lb时(以uri: lb://tensquare-base为例),gateway将使用 LoadBalancerClient把tensquare-base通过eureka解析为实际的主机和端口,并进行ribbon负载均衡。
通过打印的日志可以查看到使用了负载均衡器::
2019-08-21 16:09:36.567 INFO 14028 --- [ctor-http-nio-2] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client tensquare-base initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=tensquare-base,current list of Servers=[192.168.40.1:9001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.40.1:9001; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@1a7f45de
2019-08-21 16:09:37.545 INFO 14028 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: tensquare-base.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
提示:关于Ribbon负载均衡的具体策略的配置,详见之前的课程。
路由前缀
通过网关访问和转发的时候,处理前缀的问题。
添加前缀
目标:在通过网关访问的时候,自动添加额外前缀(相当于隐藏了部分路径),并转发到具体服务上。
在gateway中可以通过配置路由的过滤器PrefixPath,实现映射路径中地址的添加;
修改application.yml 文件:
spring:application:name: tensquare-gatewaycloud:gateway:routes:# 路由id,可以随意写- id: base-service# 代理的服务地址#uri: http://localhost:9001#代理的服务地址;lb表示从eureka中获取具体服务,后面的名字必须是Eureka中注册的服务的名字uri: lb://tensquare-base# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务,访问的规则
# - Path=/label/**- Path=/**# 过滤器filters:# 添加请求路径的前缀,转发到具体微服务的时候,自动加上该前置,访问时不要写- PrefixPath=/label
通过 PrefixPath=/xxx 来指定了路由要添加的前缀。
也就是:
PrefixPath=/label
如果访问:http://localhost/,转发时变成:http://localhost:9001/label
如果访问:http://localhost/1,转发时变成:http://localhost:9001/label/1
去除前缀
目标:在通过网关访问的时候,自动截取掉访问路径中的部分路径,使用截取后的路径进行转发请求。
在gateway中可以通过配置路由的过滤器StripPrefix,实现映射路径中地址的去除;
修改 application.yml 文件:
spring:application:# 应用的名字name: tensquare-gatewaycloud:gateway:#配置路由规则routes:# 路由id,可以随意写- id: base-service# 代理的服务地址
# uri: http://localhost:9001#代理的服务地址;lb表示从eureka中获取具体服务,后面的名字必须是Eureka中注册的服务的名字uri: lb://tensquare-base# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务
# - Path=/label/**
# - Path=/**- Path=/base/label/**# 过滤器filters:# 添加请求路径的前缀,转发到具体微服务的时候,自动加上该前置,访问时不要写#- PrefixPath=/label#表示过滤1个路径,2表示两个路径,以此类推。即转发时会去掉某段路径,访问还需要有。- StripPrefix=1
通过 StripPrefix=1 来指定了路由要去掉的前缀个数。如:路径 /api/user/1 将会被代理到 /user/1 。
也就是:
StripPrefix=1 http://localhost/base/label --》http://localhost:9001/label
StripPrefix=2 http://localhost/api/base/label --》http://localhost:9001/label
以此类推。
过滤器
简介
Gateway作为网关的其中一个重要功能,主要用来增强的。
它实现请求的鉴权等一系列额外功能。而这个动作往往是通过网关提供的过滤器来实现的。前面的路由前缀 章节中的功能也是使用过滤器实现的。
内置过滤器-局部和全局
Gateway自带过滤器有几十个,常见自带过滤器有:
过滤器名称 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由添加参数 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀 |
详细的说明在官网链接:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factories
Gateway的过滤器根据作用范围分为两种:
- 全局默认过滤器:会对所有的路由有效
- 局部过滤器:只对某路由有效
下面举例。
(1)配置全局默认过滤器
这些自带的过滤器可以和使用 路由前缀 章节中的用法类似,也可以将这些过滤器配置成不只是针对某个路由;而是可以对所有路由生效,也就是配置默认过滤器:
目标:使用自带的过滤器,动态在响应中添加一个头信息为X-Response-Default-MyName
,值为itcast
spring:application:name: tensquare-gatewaycloud:gateway:# 默认过滤器(全局有效),对所有路由生效default-filters:##响应头过滤器,对输出的响应设置其头部属性名称为X-Response-Default-MyName,值为itcast;#如果有多个参数多则重写一行设置不同的参数- AddResponseHeader=X-Response-Default-MyName,itcastroutes:# 路由id,可以随意写- id: base-service# 代理的服务地址#uri: http://localhost:9001#代理的服务地址;lb表示从eureka中获取具体服务,后面的名字必须是Eureka中注册的服务的名字uri: lb://tensquare-base# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务,访问的规则- Path=/base/label/**# 过滤器filters:# 添加请求路径的前缀,转发到具体微服务的时候,自动加上该前置,访问时不要写
# - PrefixPath=/label#表示过滤1个路径,2表示两个路径,以此类推。即转发时会去掉某段路径,访问还需要有。- StripPrefix=1
上述配置后,再访问 http://localhost/base/label 的话;那么可以从其响应中查看到如下信息:
(2)配置局部过滤器
之前配置的filters节点下面的配置就是局部过滤器(略)
filters:# 添加请求路径的前缀,转发到具体微服务的时候,自动加上该前置,访问时不要写#- PrefixPath=/label#表示过滤1个路径,2表示两个路径,以此类推。即转发时会去掉某段路径,访问还需要有。- StripPrefix=1
执行生命周期
Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用。
这里的 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。
使用场景
常见的应用场景如下:
请求鉴权:一般 GatewayFilterChain 执行filter方法前,如果发现没有访问权限,直接就返回空。
异常处理:一般 GatewayFilterChain 执行filter方法后,记录异常并返回。
服务调用时长统计: GatewayFilterChain 执行filter方法前后根据时间统计。
自定义过滤器
我们可以自己根据需求去定义不同功能的过滤器,在定义过滤器的时候,也有两种过滤器可以定义,分别为局部过滤器和全局过滤器:
-
局部过滤器:通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由
上;自带的过滤器都可以配置或者自定义按照自带过滤器的方式。如果配置
spring.cloud.gateway.default-filters 上会对所有路由生效也算是全局的过滤器;但是这些过滤器
的实现上都是要实现GatewayFilterFactory接口。比如:StripPrefixGatewayFilterFactory
-
全局过滤器:不需要在配置文件中配置,作用在所有的路由上;实现 GlobalFilter 接口即可。
自定义局部过滤器
需求:在application.yml中对某个路由配置过滤器,该过滤器可以:
在控制台输出配置文件中指定名称的请求参数的值。
1)编写过滤器
编写过滤器工厂类MyParamGatewayFilterFactory
注意:类名有规则:XxxGatewayFilterFactory,即类名必须是以GatewayFilterFactory结尾的,Xxx随便写。
com.tensquare.gateway.filter.MyParamGatewayFilterFactory
package com.tensquare.gateway.filter;import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;//自定义局部过滤器,在控制台输出配置文件中指定名称的请求参数的值。
//注意:类名有规则:XxxGatewayFilterFactory,即类名必须是以GatewayFilterFactory结尾的,Xxx随便写。
@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {//参数的常量public static final String PARAM_NAME = "param";public MyParamGatewayFilterFactory() {super(Config.class);}//提供过滤器的具体功能@Overridepublic GatewayFilter apply(Config config) {System.out.println("===经过了MyParamGatewayFilterFactory局部过滤器====");return (exchange, chain) -> {//获取请求对象ServerHttpRequest request = exchange.getRequest();//判断,如果请求的查询参数中,有包含路由配置中的参数,则打印出来if (request.getQueryParams().containsKey(config.param)) {request.getQueryParams().get(config.param).forEach(value -> System.out.printf("----------局部过滤器输出的-----%s = %s-----%n",config.param, value));}//相当于放行,继续调用下一个过滤器return chain.filter(exchange);};// return null;}@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList(PARAM_NAME);}//配置类public static class Config {private String param;public String getParam() {return param;}public void setParam(String param) {this.param = param;}}}
2)修改配置文件
在修改application.yml 配置文件
spring:application:name: tensquare-gatewaycloud:gateway:# 默认过滤器(全局有效),对所有路由生效default-filters:##响应头过滤器,对输出的响应设置其头部属性名称为X-Response-Default-MyName,值为itcast;#如果有多个参数多则重写一行设置不同的参数- AddResponseHeader=X-Response-Default-MyName,itcastroutes:# 路由id,可以随意写- id: base-service# 代理的服务地址#uri: http://localhost:9001#代理的服务地址;lb表示从eureka中获取具体服务,后面的名字必须是Eureka中注册的服务的名字uri: lb://tensquare-base# 断言规则predicates:# 路径规则,默认会将路径地址拼到代理服务地址上去请求代理的服务,访问的规则- Path=/base/label/**# 过滤器filters:# 添加请求路径的前缀,转发到具体微服务的时候,自动加上该前置,访问时不要写
# - PrefixPath=/label#表示过滤1个路径,2表示两个路径,以此类推。即转发时会去掉某段路径,访问还需要有。- StripPrefix=1# 自定义过滤器,自定义过滤器的命名应该为:***GatewayFilterFactory- MyParam=username
测试访问:http://localhost/base/label?username=Rose 检查后台是否输出name和itcast;
但是若访问: http://localhost/base/label?username2=Rose 则是不会输出的。
自定义全局过滤器
需求:模拟一个登录的校验。
基本逻辑:如果请求中有token查询参数,则认为请求有效,放行。
编写全局过滤器类MyGlobalFilter
com.tensquare.gateway.filter.MyGlobalFilter
package com.tensquare.gateway.filter;import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;//自定义全局过滤器,实现必须有token测查询参数才能放行转发
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {//过滤器增强代码功能@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("-----------------全局过滤器MyGlobalFilter---------------------");//从查询参数中去获取tokenString token = exchange.getRequest().getQueryParams().getFirst("token");//判断是否为空if (StringUtils.isBlank(token)) {//响应一个未授权的状态码exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//响应结束,直接返回,不再向下转发了。return exchange.getResponse().setComplete();}//放行效果return chain.filter(exchange);}@Override//过滤器的执行顺序,数字越小越先执行public int getOrder() {return 0;}
}
浏览器来测试:
#权限不足,返回401状态码
http://localhost/base/label?username2=Rose
#添加token参数,可正常访问
http://localhost/base/label?username2=Rose&token=xxx
负载均衡和熔断(了解)
Gateway中默认就已经集成了Ribbon负载均衡和Hystrix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议手动进行配置:
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 6000
ribbon:ConnectTimeout: 1000ReadTimeout: 2000MaxAutoRetries: 0MaxAutoRetriesNextServer: 0
Gateway跨域配置
一般网关都是所有微服务的统一入口,必然在被调用的时候会出现跨域问题。
跨域:在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果。
如:从在http://localhost:9090中的js访问 http://localhost:9000的数据,因为端口不同,所以也是跨域请求
在访问Spring Cloud Gateway网关服务器的时候,出现跨域问题的话;可以在网关服务器中通过配置解决,允许哪些服务是可以跨域请求的;具体配置如下:
spring:application:# 应用的名字name: tensquare-gatewaycloud:gateway:# 全局的CORS跨域请求配置处理globalcors:corsConfigurations:# 哪些请求资源走下面的跨域规则,这里是所有'[/**]':#允许哪些域跨域访问,allowedOrigins: * # 这种写法或者下面的都可以,*表示全部allowedOrigins:- "http://docs.spring.io"#允许哪些请求方法跨域allowedMethods:- GET
上述配置表示:可以允许来自 http://docs.spring.io 的get请求方式获取服务数据。
allowedOrigins 指定允许访问的服务器地址,如:http://localhost:10000 也是可以的。
‘[/**]’ 表示对所有访问到网关服务器的请求地址
官网具体说明:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/multi/multi__cors_configuration.html
Gateway的高可用(了解)
启动多个Gateway服务,自动注册到Eureka,形成集群。如果是服务内部访问,访问Gateway,自动负载均衡,没问题。
但是,Gateway更多是外部访问,PC端、移动端等。它们无法通过Eureka进行负载均衡,那么该怎么办?
此时,可以使用其它的服务网关,来对Gateway进行代理。比如:Nginx
Gateway与Feign的区别
Gateway 作为整个应用的流量入口,接收所有的请求,如PC、移动端等,并且将不同的请求转发至不同的处理
微服务模块,其作用可视为nginx;大部分情况下用作权限鉴定、服务端流量控制
Feign 则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用
4. SpringCloudConfig分布式配置管理
4.1 简介
在分布式系统中,由于服务数量巨多,配置文件分散在不同的微服务项目中,管理不方便。为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。
在spring cloud config 组件中,分两个角色,一是config server,二是config client。
Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储。
Config Client是Config Server的客户端,用于操作存储在Config Server中的配置内容。微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。
详细内容看在线文档: https://springcloud.cc/spring-cloud-config.html
4.2 配置服务端
4.2.1 将配置文件提交到码云
使用GitHub时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的情况。如果我们希望体验Git飞一般的速度,可以使用国内的Git托管服务——码云(gitee.com)。
和GitHub相比,码云也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务。
步骤:
(1)浏览器打开gitee.com
,注册用户 ,注册后登陆码云管理控制台
(2)在码云上创建项目tensquare-config(点击右上角的加号 ,下拉菜单选择创建项目)
(3)上传配置文件:将各个微服务的application.yml配置文件按照一定的规则更改后,上传到码云。
文件命名规则:
{application}-{profile}.yml
{application}-{profile}.properties
- application为应用名称
- profile指的运行环境(用于区分开发环境dev,测试环境test、生产环境prod等)
我们将微服务的所有的配置文件,都按照命名规则,上传到码云,
具体操作例如,将tensquare_base工程的application.yml改名为base-dev.yml后上传
地址为:https://gitee.com/bobolaoshi/tensquare-config.git
【提示】
配置文件可以在云端在线直接修改,非常方便。
4.2.2 配置中心微服务
(1)创建工程模块 配置中心微服务 tensquare_config,pom.xml引入依赖
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency></dependencies>
(2)编写配置文件application.yml
server:#默认配置中心的端口是8888port: 8888
spring:application:name: tensquare-config #服务名cloud:config:server:#Git远程仓库配置git:# 仓库地址uri: https://gitee.com/bobolaoshi/tensquare-config-072.git# 连接git的账号#username: xxx# 连接git的米面#password: xxx# 路径前缀的添加,默认为空 #prefix: myconfig
提示:
- 配置中心的默认端口是8888
- Git的uri等根据实际情况来配置
(3)创建启动类ConfigServerApplication
com.tensquare.config.ConfigServerApplication
package com.tensquare.config;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication//开启配置服务
@EnableConfigServer
public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}
}
(4)启动服务,
用浏览器测试:http://localhost:8888/base-dev.yml 可以看到配置内容
4.3 配置客户端
配置每一个客户端,下面以tensquare_base为例:
(1)在tensquare_base工程添加依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency>
(2)添加bootstrap.yml ,删除或重命名application.yml
spring:cloud:config:#应用(application)的名字name: base#环境(profile)标识profile: dev#分支的名字label: master#配置中心的地址uri: http://localhost:8888
SpringBoot默认的配置文件,优先application.yaml,主要用于做引导型的配置,application.yaml做应用型的配置
uri默认读取的是:http://localhost:8888
bootstrap.yml文件也是Spring Boot的默认配置文件,而且其加载的时间相比于application.yml更早。
application.yml和bootstrap.yml虽然都是Spring Boot的默认配置文件,但是定位却不相同。bootstrap.yml
可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。application.yml 可以用来定义应用级别的
参数,如果搭配 spring cloud config 使用,application.yml 里面定义的文件可以实现动态替换。
总结就是,bootstrap.yml文件相当于项目启动时的引导文件,内容相对固定。application.yml文件是微服务
的一些常规配置参数,变化比较频繁。
(3)测试: 启动工程tensquare_config、tensquare_eureka、、ensquare_base,看是否可以正常运行
http://localhost:9001/label
【扩展了解】
SpringCloudConfig配置中心服务也可以注册到Eureka中,让具体业务的微服务(如base微服务)去通过Eureka找到配置中心,然后再获取配置信息。但这样会导致项目结构变得更复杂。上面的例子的配置中心是个独立的服务。
4.4 配置中心的架构优化-高可用
问题:配置中心最好也注册到注册中心,而不是直接暴露给具体微服务。
第一步:将Config配置中心在Eureka注册中心中注册。
1)配置中心引入坐标:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2)在application.yml中引入eureka的配置:
eureka:client:#Eureka服务的地址service-url:defaultZone: http://localhost:8761/eureka/instance:#用于表示在猜测主机名时,服务器的IP地址应该与操作系统报告的主机名相对应。(注册服务和客户端如果在一台机器上则无需配置)prefer-ip-address: true
3)在启动类上添加注解:
//Eureka客户端
//@EnableEurekaClient
@EnableDiscoveryClient
4)重启Config配置中心,查看Eureka中是否注册了。
第二步:修改具体微服务(Base)的配置。
bootstrap.yml
spring:cloud:config:#应用的名字name: base#环境标识profile: dev#默认git分支label: master#配置中心的地址#uri: http://localhost:12000discovery:#开启Eureka的注册的服务的发现enabled: true#在Eureka中注册的配置中心服务端的名字service-id: tensquare-config
测试,ok
【Eureka注册中心配置的bug问题】
base微服务的配置有个bug,就是Eureka服务没有配置。
如果不配置Eureka服务地址,则默认读取的是http://localhost:8761:eureka
,本机测试不会出问题,但如果是分开部署,则会报错,提示,找不到eureka服务端,从而找不到配置中心主机,造成拉取配置失败 。
解决:
将eureka配置放到bootstrap.yml中配置,并删除base-dev.yml中eureka的配置。
参考配置:
eureka:client:#Eureka服务的地址service-url:defaultZone: http://localhost:8761/eureka/instance:#用于表示在猜测主机名时,服务器的IP地址应该与操作系统报告的主机名相对应。(注册服务和客户端如果在一台机器上则无需配置)prefer-ip-address: true
spring:cloud:config:#应用的名字name: base#环境标识profile: dev#分支的名字label: master#配置中心的地址#uri: http://localhost:12000discovery:#开启Eureka的注册的服务的发现enabled: true#在Eureka中注册的配置中心服务端的名字service-id: tensquare-config
5. SpringCloudBus服务总线
5.1 SpringCloudBus简介
前面已经完成了将微服务中的配置文件集中存储在远程Git仓库,并且通过配置中心微服务从Git仓库拉取配置文件,当用户微服务启动时会连接配置中心获取配置信息从而启动用户微服务。
如果我们更新Git仓库中的配置文件,那用户微服务是否可以及时接收到新的配置信息并更新呢
如果我们更新码云中的配置文件,那客户端工程是否可以及时接受新的配置信息呢?
我们现在来做有一个测试,修改一下码云中的配置文件中mysql的端口 或微服务端口,然后测试http://localhost:9001/label ,数据依然可以查询出来,证明修改服务器中的配置并没有更新立刻到工程,只有重新启动程序才会读取配置。
那我们如果想在不重启微服务的情况下更新配置如何来实现呢?
我们使用SpringCloudBus来实现配置的自动更新。
准备RabbitMQ(Docker)
(1)下载镜像:(此步省略)
#带管理插件并自动开启(推荐)
docker pull rabbitmq:3.7.16-management
#不带管理插件
docker pull rabbitmq:3.7.16
(2)创建容器,rabbitmq需要有映射以下端口: 5671 5672 4369 15671 15672 25672
15672 (if management plugin is enabled)
15671 management监听端口
5672, 5671 (AMQP 0-9-1 without and with TLS)
4369 (epmd) epmd 代表 Erlang 端口映射守护进程
25672 (Erlang distribution)
#启动带管理插件的
docker run -id --name=tensquare_rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 rabbitmq:3.7.16-management
#启动不带管理插件的
docker run -id --name=tensquare_rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 rabbitmq:3.7.16
浏览器访问 http://192.168.40.141:15672
4.2 配置服务端
(1)修改tensquare_config工程的pom.xml,引用依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-bus</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency>
(2)修改application.yml ,添加配置(rabbitmq是在spring的节点下配置)
#MQ配置rabbitmq:host: 192.168.40.141#port: 5672#username: guest#password: guest
#暴露触发消息总线的地址
management:endpoints:web:exposure:# 暴露触发消息总线的地址include: bus-refresh
4.3 配置客户端
我们还是以基础模块为例,加入消息总线
(1)修改tensquare_base工程 ,引入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-bus</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
(2)在码云的配置文件base-dev.yml中配置rabbitMQ的地址,在spring节点下添加
#MQ配置rabbitmq:host: 192.168.40.141
(2)启动tensquare_eureka 、tensquare_config、和tensquare_base 看是否正常运行
GET http://localhost:9001/label
(3)修改码云上的配置文件 ,将数据库连接IP 改为127.0.0.1 ,在本地部署一份数据库。
(4)配置中心主动发出通知,这里使用postman发出POST请求
POST http://127.0.0.1:8888/actuator/bus-refresh
(5)再次观察输出的数据是否是读取了本地的mysql数据,而无需手动重启服务(其实是自动重启了)
说明:
1、Postman或者RESTClient是一个可以模拟浏览器发送各种请求(POST、GET、PUT、DELETE等)的工具。
2、请求地址http://127.0.0.1:8888/actuator/bus-refresh中 /actuator是固定的,/bus-refresh对应的是配置中心config-server中的application.yml文件的配置项include的内容。
3、请求http://127.0.0.1:8888/actuator/bus-refresh地址的作用是访问配置中心的消息总线服务,消息总线
服务接收到请求后会向消息队列中发送消息,各个微服务会监听消息队列。当微服务接收到队列中的消息后,会重新从配置中心获取最新的配置信息。
5. Spring Cloud 体系技术综合应用概览
resh
## 4.3 配置客户端我们还是以基础模块为例,加入消息总线(1)修改tensquare_base工程 ,引入依赖```xml<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-bus</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
(2)在码云的配置文件base-dev.yml中配置rabbitMQ的地址,在spring节点下添加
#MQ配置rabbitmq:host: 192.168.40.141
(2)启动tensquare_eureka 、tensquare_config、和tensquare_base 看是否正常运行
GET http://localhost:9001/label
(3)修改码云上的配置文件 ,将数据库连接IP 改为127.0.0.1 ,在本地部署一份数据库。
(4)配置中心主动发出通知,这里使用postman发出POST请求
POST http://127.0.0.1:8888/actuator/bus-refresh
(5)再次观察输出的数据是否是读取了本地的mysql数据,而无需手动重启服务(其实是自动重启了)
说明:
1、Postman或者RESTClient是一个可以模拟浏览器发送各种请求(POST、GET、PUT、DELETE等)的工具。
2、请求地址http://127.0.0.1:8888/actuator/bus-refresh中 /actuator是固定的,/bus-refresh对应的是配置中心config-server中的application.yml文件的配置项include的内容。
3、请求http://127.0.0.1:8888/actuator/bus-refresh地址的作用是访问配置中心的消息总线服务,消息总线
服务接收到请求后会向消息队列中发送消息,各个微服务会监听消息队列。当微服务接收到队列中的消息后,会重新从配置中心获取最新的配置信息。
这篇关于02-微服务架构集大成者-SpringCloud的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!