SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由_zhuyu19911016520-CSDN博客

标签: | 发表时间:2020-11-01 08:53 | 作者:
出处:https://blog.csdn.net

前面分别对 Spring Cloud Zuul 与 Spring Cloud Gateway 进行了简单的说明,它门是API网关,API网关负责服务请求路由、组合及协议转换,客户端的所有请求都首先经过API网关,然后由它将匹配的请求路由到合适的微服务,是系统流量的入口,在实际生产环境中为了保证高可靠和高可用,尽量避免重启,如果有新的服务要上线时,可以通过动态路由配置功能上线。

本篇拿 Spring Cloud Gateway 为例,对网关的动态路由进行简单分析,下一篇将分享动态路由的进阶实现

1.gateway配置路由主要有两种方式,1.用yml配置文件,2.写在代码里。而无论是 yml,还是代码配置,启动网关后将无法修改路由配置,如有新服务要上线,则需要先把网关下线,修改 yml 配置后,再重启网关
  • yml配置方式
    在这里插入图片描述
  • 代码配置方式
    在这里插入图片描述
2.gateway网关启动时,路由信息默认会加载内存中,路由信息被封装到 RouteDefinition 对象中,配置多个RouteDefinition组成gateway的路由系统,仔细的同学可能看到RouteDefinition中的字段与上面代码配置方式比较对应
  • RouteDefinition对象在 org.springframework.cloud.gateway.route包下,其定义如下:
    在这里插入图片描述
  • RouteDefinitionLocator是个接口,在org.springframework.cloud.gateway.route包下,如果想查看网关中所有的路由信息,调用此接口方法是一个办法,需要从先注入到容器,后面还有另外一种查看方式,是Spring Cloud Gateway 的Endpoint端点提供的方法
    在这里插入图片描述
3.Spring Cloud Gateway 提供了 Endpoint 端点,暴露路由信息,有获取所有路由、刷新路由、查看单个路由、删除路由等方法,源码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中,想访问端点中的方法需要添加 spring-boot-starter-actuator 注解,并在配置文件中暴露所有端点
      # 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
4.动态路由代码实现

前提:需要启动 3个服务,eureka、gateway、consumer-service

  • 1.eureka使用前面博客中的代码
  • 2.consumer-service是个web项目,提供一个hello方法,需注册到eureka上
  • 3.新建gateway,添加引用
      <dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 4.添加基本配置和注册到eureka,不要配置路由信息映射到consumer-service,由后面的动态路由功能路由过去
  • 5.根据Spring Cloud Gateway的路由模型定义数据传输模型,分别是:路由模型、过滤器模型、断言模型
      //1.创建路由模型
public class GatewayRouteDefinition {
    //路由的Id
    private String id;
    //路由断言集合配置
    private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
    //路由过滤器集合配置
    private List<GatewayFilterDefinition> filters = new ArrayList<>();
    //路由规则转发的目标uri
    private String uri;
    //路由执行的顺序
    private int order = 0;
    //此处省略get和set方法
}

//2.创建过滤器模型
public class GatewayFilterDefinition {
    //Filter Name
    private String name;
    //对应的路由规则
    private Map<String, String> args = new LinkedHashMap<>();
    //此处省略Get和Set方法
}

//3.路由断言模型
public class GatewayPredicateDefinition {
    //断言对应的Name
    private String name;
    //配置的断言规则
    private Map<String, String> args = new LinkedHashMap<>();
    //此处省略Get和Set方法
}
  • 6.编写动态路由实现类,需实现ApplicationEventPublisherAware接口
      /**
 * 动态路由服务
 */
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware{

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }


    //增加路由
    public String add(RouteDefinition definition) {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }
    //更新路由
    public String update(RouteDefinition definition) {
        try {
            delete(definition.getId());
        } catch (Exception e) {
            return "update fail,not find route  routeId: "+definition.getId();
        }
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "update route  fail";
        }
    }
    //删除路由
    public Mono<ResponseEntity<Object>> delete(String id) {
        return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
            return Mono.just(ResponseEntity.ok().build());
        })).onErrorResume((t) -> {
            return t instanceof NotFoundException;
        }, (t) -> {
            return Mono.just(ResponseEntity.notFound().build());
        });
    }
}
  • 7.编写 Rest接口,通过这些接口实现动态路由功能,注意SpringCloudGateway使用的是WebFlux不要引用WebMvc
      @RestController
@RequestMapping("/route")
public class RouteController {

    @Autowired
    private DynamicRouteServiceImpl dynamicRouteService;

    //增加路由
    @PostMapping("/add")
    public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
        String flag = "fail";
        try {
            RouteDefinition definition = assembleRouteDefinition(gwdefinition);
            flag = this.dynamicRouteService.add(definition);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }
    //删除路由
    @DeleteMapping("/routes/{id}")
    public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
        try {
            return this.dynamicRouteService.delete(id);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    //更新路由
    @PostMapping("/update")
    public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
        RouteDefinition definition = assembleRouteDefinition(gwdefinition);
        return this.dynamicRouteService.update(definition);
    }

    //把传递进来的参数转换成路由对象
    private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(gwdefinition.getId());
        definition.setOrder(gwdefinition.getOrder());

        //设置断言
        List<PredicateDefinition> pdList=new ArrayList<>();
        List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();
        for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {
            PredicateDefinition predicate = new PredicateDefinition();
            predicate.setArgs(gpDefinition.getArgs());
            predicate.setName(gpDefinition.getName());
            pdList.add(predicate);
        }
        definition.setPredicates(pdList);

        //设置过滤器
        List<FilterDefinition> filters = new ArrayList();
        List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters();
        for(GatewayFilterDefinition filterDefinition : gatewayFilters){
            FilterDefinition filter = new FilterDefinition();
            filter.setName(filterDefinition.getName());
            filter.setArgs(filterDefinition.getArgs());
            filters.add(filter);
        }
        definition.setFilters(filters);

        URI uri = null;
        if(gwdefinition.getUri().startsWith("http")){
            uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
        }else{
            // uri为 lb://consumer-service 时使用下面的方法
            uri = URI.create(gwdefinition.getUri());
        }
        definition.setUri(uri);
        return definition;
    }
}
  • 8.启动项目,查看网关路由信息,访问 localhost:9999/actuator/gateway/routes,因没有配置路由信息,因此返回结果为空数组
    在这里插入图片描述
  • 9.通过Postman发一个 post 请求新增路由,接口地址: http://localhost:9999/route/update,路由到consumer-service 上,然后通过网关访问查看是否转发请求了(这里直接调用的update,有就会覆盖,没有则新增)
    在这里插入图片描述
  • 10.再访问 localhost:9999/actuator/gateway/routes ,可以看到新的路由信息已经配置进去了,这就是动态路由配置,还可以调用删除、修改接口,操作动态操作路由信息
    在这里插入图片描述
  • 11.配置路由信息后,访问consumer-service服务,正常返回,说明路由已经生效,请求转发到consumer-service服务
    在这里插入图片描述
    好了,动态路由的简单实现了,一般在生产环境不使用此方式,因为网关都是多实例部署,还可能随时增加实例,需要已调用接口的方式一一调用网关所有的实例

本篇博客参考了许进的文章,地址: http://springcloud.cn/view/407,还有他写的 《重新定义SpringCloud》比较不错

代码已上传至码云, 源码,项目使用的版本信息如下:

      - SpringBoot 2.0.6.RELEASE
- SpringCloud Finchley.SR2

下篇将介绍动态路由的进阶实现,实现方式:

  • 创建一个路由信息维护的项目
  • 实现增删改查路由信息到mysql
  • 然后发布,发布后将路由信息与版本信息保存到redis中,对外提供 rest 接口
  • 网关开启定时任务,定时拉取 rest 接口中最新版本的路由信息,这样网关发布多个实例后,都会单独的去拉取维护的路由信息
    参考了 https://blog.csdn.net/tianyaleixiaowu/article/details/83412301

相关 [springcloud 十三 gateway] 推荐:

SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由_zhuyu19911016520-CSDN博客

- -
前面分别对 Spring Cloud Zuul 与 Spring Cloud Gateway 进行了简单的说明,它门是API网关,API网关负责服务请求路由、组合及协议转换,客户端的所有请求都首先经过API网关,然后由它将匹配的请求路由到合适的微服务,是系统流量的入口,在实际生产环境中为了保证高可靠和高可用,尽量避免重启,如果有新的服务要上线时,可以通过动态路由配置功能上线.

SpringCloud Gateway与k8s_zhangjunli的博客-CSDN博客

- -
接下来的内容由以下几部分组成:. 什么是SpringCloud Gateway. SpringCloud Gateway实战参考. kubernetes上的SpringCloud Gateway. 开发k8sgatewaydemo. 什么是SpringCloud Gateway. SpringCloud Gateway是SpringCloud技术栈下的网关服务框架,在基于SpringCloud的微服务环境中,外部请求会到达SpringCloud Gateway应用,该应用对请求做转发、过滤、鉴权、熔断等前置操作,一个典型的请求响应流程如下所示:.

微服务SpringCloud之GateWay熔断、限流、重试 - 社会主义接班人 - 博客园

- -
纯洁的微笑的Spring Cloud系列博客终于学完了,也对Spring Cloud有了初步的了解. StripPrefix Filter 是一个请求路径截取的功能,我们可以利用这个功能来做特殊业务的转发. StripPrefix是当请求路径匹配到/name/**会将包含name和后边的字符串接去掉转发, StripPrefix=2就代表截取路径的个数,当访问.

聊聊 API Gateway 和 Netflix Zuul

- - ScienJus's Blog
最近参与了公司 API Gateway 的搭建工作,技术选型是 Netflix Zuul,主要聊一聊其中的一些心得和体会. 本文主要是介绍使用 Zuul 且在不强制使用其他 Neflix OSS 组件时,如何搭建生产环境的 Gateway,以及能使用 Gateway 做哪些事. 不打算介绍任何关于如何快速搭建 Zuul,或是一些轻易集成 Eureka 之类的的方法,这些在官方文档上已经介绍的很明确了.

Spring Cloud Gateway(限流) | Wind Mt

- -
限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击. 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制 MQ 的消费速率.

快速突击 Spring Cloud Gateway

- - 掘金后端
认识 Spring Cloud Gateway. Spring Cloud Gateway 是一款基于 Spring 5,Project Reactor 以及 Spring Boot 2 构建的 API 网关,是 Spring Cloud 微服务生态的主要组件之一. Spring Cloud Gateway 主要负责接口请求的路由分发,并且支持对请求的安全验证,流量监控和流量控制等扩展操作.

apache/apisix: The Cloud-Native API Gateway

- -
Health Checks: Enable health check on the upstream node and will automatically filter unhealthy nodes during load balancing to ensure system stability..

基于springcloud实现的灰度发布

- -
基于springcloud实现的灰度发布. gray-config-server 配置中心. 端口:6007,方便起见直接读取配置文件,生产环境可以读取git. 先启动配置中心,所有服务的配置(包括注册中心的地址)均从配置中心读取. gray-xxx-service 服务消费者. 调用服务提供者和服务提供者,验证是否进入灰度服务.

SpringCloud项目接入Jaeger(下) - 掘金

- -
spring-cloud-sleuth这个组件时,会面临两个问题. 首先是日志中无法显示traceId和spanId这些链路信息,其次是不能在用. spring-cloud-sleuth所提供的方式进行链路传值. spring-cloud-sleuth是将traceId等链路信息保存在. slf4j的MDC(Mapped Diagnostic Contexts)中,然后通过%X{traceId}这种方式将traceId提取出来,比如打印到控制台的默认格式是:.

Springcloud + RocketMQ 解决分布式事务

- - 掘金架构
分布式事务有哪些实现方式. 随着互联网时代的高速发展,分布式成了大型系统的标配,这是时代发展的选择. 大型分布式系统不是每个公司和开发人员都能够涉及的领域,因为大型系统后面都 隐藏着众多代名词:复杂,昂贵,高科技,人才云集,大战略. 大部分领头互联网公司甚至依托自己的分布式经验逐步建立自己的体系,并使用这套体系搭建自己的平台对内,甚至对外提供服务, 就像现在众多的云平台提供的服务,甚至有些大战略提出促进发展:大中台小前台、大炮台支援单兵作战等等.