你好,我是程序员Alan,很高兴遇见你。
我们在《系统架构设计—高并发场景微服务实战(三)》,将订票服务拆分成了很多子服务,其中很多子服务都需要用户验证、权限验证、流量控制等,那么我们要在每个子服务中重复编写相关的逻辑吗?
为什么需要网关
单体架构——客户端直接调用
它的特点是所有的服务都来自一个应用,客户端直接向服务端发送请求。
微服务架构——客户端直接调用
微服务架构中,一个大的应用被拆分成一个个小的服务,客户端直接调用会直接带来很多问题,例如:
- 鉴权、日志记录等公共操作,每个子服务都需要单独实现。
- 服务直接对外暴露,安全风险增加
- 后端服务变动前端也要相应变动
微服务架构——引入网关
在网关层针对公共操作做统一处理,避免了重复造轮子;并通过网关对外提供统一的入口供客户端访问,隐藏系统架构实现的细节;另外我们还可以对外的API使用HTTP协议,对内使用性能更高的通信协议。
建立SpringCloud Gateway服务
引入依赖
因为我们已经在父工程管理了SpingCloud版本,所以在子服务中不需要写版本号
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
配置路由
SpringCloud支持很多种路由方式,例如时间匹配,IP地址匹配,请求方式匹配,请求路径匹配等等。
今天我们使用请求路径匹配做一个简单例子。
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=gateway-env, springcloud-gateway
routes:
- id: air-shenzhen
uri: http://127.0.0.1:18802 #若使用了注册中心可以使用服务名 lb://air-shenzhen
predicates:
- Path=/api/shenzhen/**
- id: air-xiamen
uri: http://127.0.0.1:18801
predicates:
- Path=/api/xiamen/**
代码解释:
- default-filters: 在请求中加入一个自定义的header,注明这个请求经过了此网关。
- routes: 路由配置,配置了两个路由,id是服务名,uri是路由转发的地址,predicates是匹配规则
此时一个简单的SpringCloud Gateway服务就已经可以启动了,它的功能是将url为/api/shenzhen/**的请求转发到http://127.0.0.1:18802/**,将url为/api/xiamen/**的请求转发到http://127.0.0.1:18801/**,前端直接访问网关调用对应服务,不必再关心子服务的服务名、服务端口等情况。
服务限流
Gateway 组件内部默认实现了 Redis + Lua 进行限流,可以通过自定义的方式来指定是根据 IP、用户或是 URI 来进行限流。
引入相关jar包
<!--基于 reactive stream 的 redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
配置基于 IP 进行限流。
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 3 #允许用户每秒处理多少个请求
redis-rate-limiter.burstCapacity: 5 #令牌桶的容量,允许在一秒钟内完成的最大请求数
key-resolver: "#{@remoteAddrKeyResolver}" #SPEL 表达式去的对应的 bean
上文的 KeyResolver 配置项是用来定义按什么规则来限流,比如本次采用 IP 进行限流,编写对应的实现类实现此接口:
public class AddrKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
在启动类进行 @Bean 定义:
@Bean
public AddrKeyResolver addrKeyResolver() {
return new AddrKeyResolver();
}
跨域支持
代码方式
@Configuration
public class CORSConfiguration {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(Boolean.TRUE);
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addExposedHeader("setToken");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
配置文件配置
spring:
cloud:
gateway:
discovery:
# 跨域
globalcors:
corsConfigurations:
'[/**]':
allowedHeaders: "*"
allowedOrigins: "*"
# 为保证请求的安全,项目中只支持 get 或 post 请求,其它请求全部屏蔽,以免导致多余的问题
allowedMethods:
- POST
站在巨人的肩膀上:
- 码闻强—《SpringCloud微服务实战》