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请求不同服务次数增加,性能不高

  • 网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等

网关静态路由

  1. 搭建网关模块
  2. 引入依赖:starter-gateway
  3. 编写启动类
  4. 编写配置文件启动测试
  5. 启动测试
  • 准备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为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护 HystrixCommand的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值
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核心的功能也是通过内置的全局过滤器来完成。
  • 自定义全局过滤器步骤:
    1. 定义类实现GlobalFilter和Ordered接口
    2. 复写方法
    3. 完成逻辑处理

在这里插入图片描述

  • 在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测试

posted @ 2021-08-08 16:48  HumorChen99  阅读(7)  评论(0编辑  收藏  举报  来源