SpringCloud(五)- GateWay简介及GlobalFilter 过滤器的使用

唯能极于情,故能极于剑

本文转载于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了,不来点 硬核 咋行呢,下面小编就和大家聊聊怎么用 GateWayGlobalFilter

注意:有 服务端网关 两个模块/项目

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上,供大家下载、分享、交流、指正,下面是源码地址:

GitHubhttps://github.com/msxdlb/Spring-Cloud-2020

有问题或错误请及时联系小编或关注小编公众号 “CodeCow”,小编一定及时回复和改正 啦

《 谋事在人,成事在天 》 你只管努力——其他的交给天意

2020/04/23 傍晚 18:29

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值