微服务网关Gateway
1.微服务网关Gateway
1.1 微服务网关概述
不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性
- 存在跨域请求,在一定场景下处理相对复杂
- 认证复杂,每个服务都需要独立认证
- 难以重构
网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过网关这一层。这样安全、性能、监控可以交由网关来做。
微服务网关的优点
- 安全 ,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
- 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。(日志的监控与分析)
- 易于统一认证授权。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数。
总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控的相关功能。
实现微服务网关的技术:
- nginx
- Zuul ,Zuul 是 Netflix 出品的一个基于 JVM 路由和服务端的负载均衡器。
- spring-cloud-gateway, 是spring 出品的 基于spring 的网关项目,集成断路器,路径重写,性能比Zuul好。
gateway官网的地址
https://spring.io/projects/spring-cloud-gateway
1.2 微服务网关微服务搭建
步骤
- 创建gateway的maven工程
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 在启动类上添加注解
@EnableEurekaClient
- 在resources下创建application.yml
spring:
cloud:
gateway:
routes:
- id: after_route #要跳转的服务名
uri: lb://goods #lb:在eureka上找到goods服务,然后通过后面的网址跳转
predicates:
‐ Path=/goods/** #goods后面的请求全部通过
filters:
‐ StripPrefix= 1 #访问时将goods省略掉(如果不明白,就把这个写上就行)
1.3 微服务网关跨域
修改application.yml ,在spring.cloud.gateway节点添加配置:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
1.4 微服务网关过滤器
我们可以通过网关过滤器,实现一些逻辑的处理,比如ip黑白名单拦截、特定地址的拦截等。
/**
* 获取用户ip
*/
@Component
public class IpFilter implements GlobalFilter, Ordered {
//具体逻辑业务实现
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("经过第一个过滤器");
//1.获取请求 2.获取远程地址 3.获取ip
String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName();
System.out.println("ip:" + ip);
//放行
return chain.filter(exchange);
}
//当前过滤器级别
@Override
public int getOrder() {
return 1;
}
}
以上代码是获取用户请求的地址,并没有实现具体逻辑。
2.网关限流
限流:当我们的系统被频繁的请求的时候,就有可能将系统压垮。
所以为了解决这个问题,需要在每一个微服务中做限流操作。
但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。
2.1思路分析
2.2令牌桶算法
概述:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
- 根据限流大小,设置按照一定的速率往桶里添加令牌;
- 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
- 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流。
2.3令牌桶思路分析
2.4限流代码思路
需求:每个ip地址1秒内只能发送1次请求,多出来的请求返回429错误。
- 添加依赖, gateway 默认使用redis的RateLimter限流算法来实现。
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
- 定义KeyResolver
在GatewayApplicatioin
引导类中添加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver来指定限流的Key。
//指定限流的key
@Bean
public KeyResolver keyResolver() {
return exchange -> {
//根据ip限流
String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName();
return Mono.just(ip);
};
}
- 修改application.yml中配置项,指定限制流量的配置以及REDIS的配置
routes:
- id: goods
uri: lb://goods
predicates:
- Path=/goods/**
filters:
- StripPrefix= 1
- name: RequestRateLimiter #请求数限流 名字不能随便写
args:
key-resolver: "#{@keyResolver}"
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
redis-rate-limiter.burstCapacity: 1 #令牌桶总容量
以上是在原来的配置文件中修改,在predicates:
下添加依赖。
-
burstCapacity:令牌桶总容量。
-
replenishRate:令牌桶每秒填充平均速率。
-
key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
-
在配置文件中配置redis
redis:
host: 192.168.200.128
port: 6379
以上是针对每一条请求都做了限流,针对单独某一条ip做限流,需要在KeyResolver
配置单独的ip。