唯能极于情,故能极于剑
本文转载于:http://www.codecow.cn/
此文由四部分组成(GateWay简介、GlobalFilter过滤器 使用、实操、debug测试、总结),别着急,慢慢来
文章目录
一、GateWay
1.1、GateWay 啥玩意 ?
- 官网:GateWay 是在Spring生态系统之上构建的API网关服务,基于Spring 5, Spring Boot 2 和 Project Reactor 等技术;SpringCloud GateWay 是基于WebFlux框架实现的,而WebFlux框架底层则是使用了高性能的Reactor模式通信框架Netty
- 目的:GateWay 旨在提供一种简单而有效的方式来对API进行路由,提供统一的路由方式且基于Filter 链的方式提供了网关基本的功能,例如:熔断、限流、重试等。。。。。
1.2、GateWay 能干嘛 ?
- 反向代理
- 鉴权
- 流量监控
- 熔断
- 日志监控
。。。。。。
1.2、GateWay 三大核心 ?
①、路由(Route)
- 路由是构建网关的基本模块,它由ID,目标URI,一些列的断言和过滤器组成,如果断言为 true 则匹配该路由
②、断言(Predicate)
- 匹配 HTTP 请求中的所有内容(例如:请求头或请求参数),如果请求与断言相匹配则进行路由
③、过滤(Filter)
- 指的是 Spring 框架中 GateWayFilter 的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
1.3、GateWay 怎么玩 ?
官网:https://cloud.spring.io/spring-cloud-static/Hoxton.SR3/reference/htmlsingle/#gateway-request-predicates-factories
哈哈,看过官网的都懵逼吧,全是 “English” 不说了,还 TM 至少有100多页吧 !!
别急小编带你细品 GateWay
二、GateWay 实操
了解 GateWay了,不来点 硬核 咋行呢,下面小编就和大家聊聊怎么用 GateWay 和 GlobalFilter
注意:有 服务端 和 网关 两个模块/项目
2.1、网关 — 操作(细看 GlobalFilter 过滤器使用)
①、首先导包
注意: SpringCloud 版本为:Hoxton.SR3 版本之间差异非常大,其他版本小编未进行测试
为什么要用此版本 ? 因为使用 GlobalFilter 也遇到过一些坑 ^ _ ^
<!--监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册与发现 consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--服务调用 openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
②、改 YML 配置文件
server:
port: 9527 #网关端口号
spring:
application:
name: cloud-gateway #服务名
cloud:
consul:
host: localhost #consul的IP
port: 8500 #consul启动端口默认8500
discovery:
service-name: ${spring.application.name}
prefer-ip-address: true #不写这个配置,在docker下的consul里面健康检查会失败
healthCheckInterval: 5s #健康检查频率
port: ${server.port} #注册服务所在端口
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_route_1 #路由id,随便写,建议配合服务名
# uri: http://localhost:8888 #一定要匹配提供服务的路由地址,自测可以用这个URI
uri: lb://cloud-hystrix-provider-service #由于要进行负载均衡,所以用服务名(来自下面 服务端操作 YML)
predicates:
- Path=/payment/** #断言,路径想匹配的进行路由
- Header=token, cloud-gateway #断言,请求头的key为token,value为cloud-gateway
③、添加主启动类
@SpringBootApplication
@EnableFeignClients // 作用:启用feign客服端
public class GateWayMain {
public static void main(String[] args) {
SpringApplication.run(GateWayMain.class, args);
}
}
④、过滤器_1 GateWayFilter
/**
* Create By LB on 2020/4/22.
*/
@Component
@Slf4j
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; //高优先级的过滤器,防止某些系统内置的过滤器可能也会去读body,导致只能读一次的情况
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("========= 进入网关 过滤器 ==================");
MediaType contentType = exchange.getRequest().getHeaders().getContentType(); //获取类型 application/json
if (contentType == null) {
return chain.filter(exchange);
} else {
String token = exchange.getRequest().getHeaders().getFirst("token"); //获取请求头里面的token
if(token.startsWith("cloud-gateway")){
// 进行业务逻辑操作 此处略
}
Mono<DataBuffer> join = DataBufferUtils.join(exchange.getRequest().getBody());//获取上下文monoSubscriberContext
return join.flatMap(i -> { //返回一个新的Mono
DataBufferUtils.retain(i);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> //读取 body数据 Flux<DataBuffer>
Flux.just(i.slice(0, i.readableByteCount())));
ServerHttpRequestDecorator mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { //ServerHttpRequestDecorator这个请求装饰器对request进行包装
@Override
public Flux<DataBuffer> getBody() { //重写getBody方法, 后面过滤链就能实现对body的多次读取
return cachedFlux ;
}
};
ServerWebExchange mutatedExchange =
exchange.mutate().request(mutatedRequest).build(); //用新的ServerHttpRequest修改exchage,这样body就可以多次读取
return chain.filter(mutatedExchange);
});
}
}
}
⑤、过滤器_2 MyGlobalFilter
/**
* Create By LB on 2020/4/23.
*/
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder(){
return -99;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
ServerHttpRequest request = exchange.getRequest();
//获取请求体
Flux<DataBuffer> body = request.getBody();
StringBuilder sb = new StringBuilder();
body.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
sb.append(bodyString);
});
String bodyContent = resolveBodyFromRequest(exchange.getRequest()); //得到body内容,但是含有空格和换行等
//去掉空格,换行和制表符
if (bodyContent.toString() != null && bodyContent.toString().length() > 0) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(bodyContent);
bodyContent = m.replaceAll("");
}
log.info(" body 的内容为: " + bodyContent);
// body 内容都获取到了,你想干嘛干啊, 身份认证、过滤、验证、等相关逻辑
return chain.filter(exchange.mutate().build());
}
}
2.2、服务端 — 操作
①、首先导包
// 监控
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
// Web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
// 服务注册与发现 Consul
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
// 熔断器 Hystris
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
②、改 YML 配置文件
server:
port: 8888 #端口号
spring:
application:
name: cloud-hystrix-provider-service #服务名 就是网关配置文件中的:URI
cloud:
consul:
host: localhost #consul的IP
port: 8500 #consul启动端口默认8500
discovery:
service-name: ${spring.application.name}
prefer-ip-address: true #不写这个配置,在docker下的consul里面健康检查会失败
healthCheckInterval: 5s #健康检查频率
port: ${server.port} #注册服务所在端口
③、添加主启动类
/**
* 注意:@SpringCloudApplication 注解可以代替下面三个注解
*/
@SpringBootApplication // springboot 注解
@EnableDiscoveryClient // 作用:能够让Consul注册中心发现,并扫描到该服务
@EnableCircuitBreaker // SpringCloud中使用断路器,需要加上此注解
public class HystrixProviderMain8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixProviderMain8001.class, args);
}
}
④、业务逻辑Controller
@Resource
private PaymentService paymentService; //service层调用
/**
* 根据id查询
* @param id
* @return
*/
@GetMapping("/payment/hystrix/ok/{id}")
public RespResult<Payment> getPaymentById(@PathVariable("id") Integer id) {
log.info(" hystrix 8001 ok ");
Payment payment = paymentService.getById(id); //getById 是serviceImpl中根据id 获取 payment 实体的方法, 小编在这就不赘述了
if(payment == null){
return new RespResult<>(444, "查询为空");
}
return RespResult.success(payment);
}
/**
* 添加
* @param id
* @return
*/
@PostMapping("/payment/add")
public RespResult add(@RequestBody Payment payment) {
if(!StringUtils.isEmpty(payment.getSerial())){
Payment payment1 = new Payment();
payment1.setId((int) (Math.random() * 1000));
payment1.setSerial(payment.getSerial());
paymentService.save(payment1);
return RespResult.success(RespResultEnum.ADD_SUCCESS);
}
return RespResult.success(RespResultEnum.ADD_ERROR);
}
初学者说明:
Payment 实体就两个字段 id、serial
RespResult 格式:
{
"code": 200,
"message": "成功",
"data": {
"id": 1,
"serial": "我还是从前那个少年"
}
}
三、GateWay Debug测试
①、测试步骤
测试步骤:
1、启动服务端
cloud-hystrix-provider-service 端口号 8888
2、启动网关
cloud-gateway 端口号 9527
3、地址栏输入
localhost:8500 看consul上是否有上面两个服务,有的话下一步(首先你的启动 consul )
小白: 不知道怎么启动 consul 或 不知道 consul是啥的 看 小编 第一将 :
地址:https://blog.csdn.net/Msxd_/article/details/105531664
4、小编直接上图吧,看下面
小编一顿 SAO 操作, 还可以吧 ^ _ ^
②、老残式上图 ^ _ ^
图一:
图二:
图三:
对 GateWay 实操 总结: 其实就两点:
①、网关其实就像一道大门,所有访问都得经过网关
②、注意 版本 的使用, GlobalFilter 的使用
咋青山不改,绿水长流,不妨看看小编其他作品,很香哟,哈哈,加油 ^ _ ^
四、总结
这是 SpringCloud 的 GateWay 篇,后续小编会从 “ Config(分布式配置中心) 、Nacos(阿里)…” 等坚持以博客的方式来分享自己对SpringCloud 的理解,并从不同角度和大家分享工作心得,并且含有相关Demo,最终小编会发布到GitHub上,供大家下载、分享、交流、指正,下面是源码地址:
GitHub:https://github.com/msxdlb/Spring-Cloud-2020
有问题或错误请及时联系小编或关注小编公众号 “CodeCow”,小编一定及时回复和改正 啦
《 谋事在人,成事在天 》 你只管努力——其他的交给天意
2020/04/23 傍晚 18:29