SpringCloud之Gateway服务网关学习笔记步步截图(附带学习时写的源码项目)
SpringCloud
整个spring cloud学习时写的项目源码:git@gitee.com:HumorChen/spring-cloud-parent.git
上一篇博客为:eureka(服务治理)SpringCloud服务注册发现(服务治理)之Eureka学习笔记步步截图(附带学习时写的源码项目)
读这篇博客前先下载上面的git项目。
初识Spring Cloud
什么是微服务
- "微服务”一词源于Martin Fowler的名为Microservices的博文,可以在他的官方博客上找到
http://martinfowler.com/articles/microservices.html - 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过HTTP的RESTfuLAPI进行通信协作。
- 被拆分成的每一个小型服务都围绕着系统中的某一项或些耦合度较高的业务功能进行构建,并且每个服务都维护着白身的数据存储、业务开发自动化测试案例以及独立部署机制。
SpringCloud简介
-
spring cloud 是一系列框架的有序集合
-
spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟的、经得起实际考验的框架组合起来
-
通过Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
-
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务注册发现、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
-
Spring Cloud版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本: Angel,第二个Release版本: Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton
-
spring cloud版本和springboot版本对应关系
Spring Cloud 与Dubbo对比
- Spring Cloud 与 Dubbo都是实现微服务有效的工具。
- Dubbo只是实现了服务治理,而Spring Cloud子项目分别覆盖了微服务架构下的众多部件。
- Dubbo使用RPC通讯协议,Spring Cloud使用RESTful完成通信,Dubbo效率略高于Spring Cloud。
小结
- 微服务就是将项目的各个模块拆分为可独立运行、部署、测试的架构设计风格。
- Spring公司将其他公司中微服务架构常用的组件整合起来,并使用SpringBoot简化其开发、配置。称为Spring Cloud
- Spring Cloud 与Dubbo都是实现微服务有效的工具。Dubbo性能更好,而Spring Cloud功能更全面。
Gateway服务网关
网关概述
-
网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
-
在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。
-
存在的问题
客户端多次请求不同的微服务,增加客户端的复杂性认证复杂
每个服务都要进行认证
http请求不同服务次数增加,性能不高
-
-
网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等
网关静态路由
- 搭建网关模块
- 引入依赖:starter-gateway
- 编写启动类
- 编写配置文件启动测试
- 启动测试
-
准备eureka服务器模块:eureka-server-gateway,服务提供方gateway-provider,服务消费方gateway-consumer,网关服务模块api-gateway-server
-
eureka和provider,consumer全部从前面的hystrix部分里拿过来改改用,把应用名改下,然后consumer里的feign配置的接口也需要改动下服务提供方的名字,其他的基本上没啥好改的,然后启动eureka,provider,consumer并测试接口是否符合预期
-
给api-gateway-server项目增加配置文件并配置网关
application.yml
spring: profiles: active: dev application: name: api-gateway-server
application-dev.yml
server: port: 80 spring: cloud: # 网关配置 gateway: # 路由配置 routes: # 集合 # id 唯一标识,默认是UUID # uri 转发路径 # predicates 匹配条件 - id: gateway-provider uri: http://localhost:8000/ predicates: - Path=/goods/**
application-prod.yml
server: port: 80 spring: cloud: # 网关配置 gateway: # 路由配置 routes: # 集合 # id 唯一标识,默认是UUID # uri 转发路径 # predicates 匹配条件 - id: gateway-provider uri: http://localhost:8000/ predicates: - Path=/goods/**
-
给api-gateway-server项目增加启动类
package com.fpa.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class GatewayServerApp { public static void main(String[] args) { SpringApplication.run(GatewayServerApp.class,args); } }
-
启动网关gateway项目,访问http://localhost/goods/2正常出了数据证明网关起作用了转发过去了
网关动态路由
前面的如果微服务换了端口或者ip则需要重新修改网关的配置并重启,很麻烦,我们需要的是微服务那边随他怎么去换ip和端口,我们网关也能正常工作,而ip和端口eureka这个服务注册的地方是知道的,那么我们要将eureka和gateway结合使用。
-
修改api-gateway-server的配置
application-dev.yml
server: port: 80 spring: cloud: # 网关配置 gateway: # 路由配置 routes: # 集合 # id 唯一标识,默认是UUID # uri 转发路径 # predicates 匹配条件 - id: gateway-provider # 静态路由uri # uri: http://localhost:8000/ # 动态路由,根据服务名从eureka获取地址 uri: lb://gateway-provider predicates: - Path=/goods/** eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8761/eureka #eureka服务器地址
-
重启gateway项目,再次访问http://localhost/goods/2可以发现仍然可以正常获取数据,证明生效
-
测试下多个provider,我们创建一个和gateway-provider一样的模块命名为gateway-provider2,修改端口为8002,修改返回的数据中title为“华为手机gateway2”,并启动,也一起注册到eureka上。多次访问http://localhost/goods/2可以发现会依次轮询返回下面这两个,证明负载均衡也生效了。
{"id":1,"title":"华为手机gateway2","price":5999.0}
{"id":1,"title":"华为手机gateway","price":5999.0}
微服务名称配置
上面的动态路由是直接转发的,以后服务多了,可能存在前缀相同冲突。那么我们希望前面能够以不同的微服务名称进行区分,因此我们可以进行微服务名称配置。
discovery:
locator:
enabled: true # 设置路径前可以添加微服务的名字以区分
lower-case-service-id: true # 由于eureka默认都是大写的字母,因此我们打开允许服务名小写的开关
- 修改gateway项目,完整配置
server:
port: 80
spring:
cloud:
# 网关配置
gateway:
# 路由配置
routes: # 集合
# id 唯一标识,默认是UUID
# uri 转发路径
# predicates 匹配条件
- id: gateway-provider
# 静态路由uri
# uri: http://localhost:8000/
# 动态路由,根据服务名从eureka获取地址
uri: lb://gateway-provider
predicates:
- Path=/goods/**
discovery:
locator:
enabled: true # 设置路径前可以添加微服务的名字以区分
lower-case-service-id: true # 由于eureka默认都是大写的字母,因此我们打开允许服务名小写的开关
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8761/eureka #eureka服务器地址
- 重启gateway项目,访问http://localhost/gateway-provider/goods/2和http://localhost/goods/2,我们发现两个都是可以访问的,当然我们推荐第一种带服务名访问,更好区分。
Gateway过滤器
-
Gateway支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
-
Gateway提供两种过滤器方式:“pre”和“post”
-
pre过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
-
post过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。
-
Gateway还提供了两种类型过滤器
- GatewayFilter:局部过滤器,针对单个路由
- GlobalFilter :全局过滤器,针对所有路由
Gateway局部过滤器
内置的过滤器工厂
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 HystrixCommand的名称 | |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 |
内置过滤器使用演示
-
修改gateway项目配置文件
加一个参数过滤器上去增加一个参数username=zhangsan
filters: - AddRequestParameters=username,zhangsan
完整的
server: port: 80 spring: cloud: # 网关配置 gateway: # 路由配置 routes: # 集合 # id 唯一标识,默认是UUID # uri 转发路径 # predicates 匹配条件 - id: gateway-provider # 静态路由uri # uri: http://localhost:8000/ # 动态路由,根据服务名从eureka获取地址 uri: lb://gateway-provider predicates: - Path=/goods/** filters: - AddRequestParameters=username,zhangsan discovery: locator: enabled: true # 设置路径前可以添加微服务的名字以区分 lower-case-service-id: true # 由于eureka默认都是大写的字母,因此我们打开允许服务名小写的开关 eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8761/eureka #eureka服务器地址
-
给接口添加一个参数username,并打印出来
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),//超时时间 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),//监控时间黑认5000毫秒 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//失败次数。默认20次 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")//失败率默认50% })//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id,String username) { //故意制造一个异常出来 // int a = 1/0; //故意制造一个超时,因为hystrix默认也是1秒超时 /*try { Thread.sleep(2000); }catch (Exception e){}*/ System.out.println(username); //熔断器测试 if (id == 1) { int a = 1 / 0; } return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * * @param id * @return */ public Goods findOne_fallback(int id,String username) { Goods goods = new Goods(); goods.setTitle("服务降级了呀"); System.out.println(username); return goods; } }
-
访问http://localhost/gateway-provider/goods/2
Gateway全局过滤
- GlobalFilter全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。
- Spring Cloud Gateway核心的功能也是通过内置的全局过滤器来完成。
- 自定义全局过滤器步骤:
- 定义类实现GlobalFilter和Ordered接口
- 复写方法
- 完成逻辑处理
-
在api-gateway-server中建立一个类MyGlobalFilter实现GlobalFilter接口和Ordered接口
package com.fpa.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("自定义全局过滤器执行了~"); return chain.filter(exchange);//放行,继续执行过滤器链 } /** * 数值越小越线制性 * @return */ @Override public int getOrder() { return 0; } }
-
重启gateway项目,访问http://localhost/gateway-provider/goods/2测试
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039559