如何动态请求参数网关? | 带你读《Spring Cloud Alibaba(2019)》之十二

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 本节介绍动态请求网关,GateWay的词汇表有那些,GateWay解决跨域的问题,网关GateWay源码分析,常见错误

上一篇:如何保证微服务接口的安全? | 带你读《Spring Cloud Alibaba(2019)》之十一
下一篇:什么是SpringCloud Sentinel | 带你读《Spring Cloud Alibaba(2019)》之十三

本文来自于《精通Spring Cloud Alibaba》课程的整理,讲师为余胜军,点击查看视频内容
本文系志愿者整理,供配合学习中心课程使用,不做商业用途。

动态请求参数网关

动态网关:任何配置都实现不用重启网关服务器都可以及时刷新网关配置。
方案:
1.基于数据库形式实现,特别建议,阅读性高
2.基于配置中心实现,不建议使用,需要定义json格式配置,阅读性差

注意:配置中心实现维护性比较差,建议采用数据库形式设计。

基于数据库表形式的设计
网关已经提供了API接口
1、直接新增
2、直接修改

思路:
默认加载时候
1、当我们的网关服务启动的时候,从我们数据库查询网关的配置。
2、将数据库的内容读取到网关内存中

网关配置要更新的时候,需要同步调用

伪代码步骤:
1、更新数据库
2、调用网关api更新

网关服务相关表

CREATE TABLE `mayikt_gateway` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `route_id` varchar(11) DEFAULT NULL,
  `route_name` varchar(255) DEFAULT NULL,
  `route_pattern` varchar(255) DEFAULT NULL,
  `route_type` varchar(255) DEFAULT NULL,
  `route_url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

代码实现动态服务网关过程

public class GatewayService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    @Autowired
    private MayiktGatewayMapper mayiktGateway;

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

    public void initAllRoute() {
        // 从数据库查询配置的网关配置
        List<GateWayEntity> gateWayEntities = mayiktGateway.gateWayAll();
        for (GateWayEntity gw :
                gateWayEntities) {
            loadRoute(gw);
        }

    }


    public String loadRoute(GateWayEntity gateWayEntity) {
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        FilterDefinition filterDefinition = new FilterDefinition();
        Map<String, String> filterParams = new HashMap<>(8);
        // 如果配置路由type为0的话 则从注册中心获取服务
        URI uri = null;
        if (gateWayEntity.getRouteType().equals("0")) {
            URI  uri = UriComponentsBuilder.fromUriString("lb://" + gateWayEntity.getRouteUrl() + "/").build().toUri();
        } else {
            uri = UriComponentsBuilder.fromHttpUrl(gateWayEntity.getRouteUrl()).build().toUri();
        }
        // 定义的路由唯一的id
        definition.setId(gateWayEntity.getRouteId());
        predicate.setName("Path");
        //路由转发地址
        predicateParams.put("pattern", gateWayEntity.getRoutePattern());
        predicate.setArgs(predicateParams);

        // 名称是固定的, 路径去前缀
        filterDefinition.setName("StripPrefix");
        filterParams.put("_genkey_0", "1");
        filterDefinition.setArgs(filterParams);
        definition.setPredicates(Arrays.asList(predicate));
        definition.setFilters(Arrays.asList(filterDefinition));
        definition.setUri(uri);
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }


}

实体类&访问层

public interface MayiktGatewayMapper {

    @Select("SELECT ID AS ID, route_id as routeid, route_name as routeName,route_pattern as routePattern\n" +
            ",route_type as routeType,route_url as routeUrl\n" +
            " FROM mayikt_gateway\n")
    public List<GateWayEntity> gateWayAll();

    @Update("update mayikt_gateway set route_url=#{routeUrl} where route_id=#{routeId};")
    public Integer updateGateWay(@Param("routeId") String routeId, @Param("routeUrl") String routeUrl);
}

Maven依赖

    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 阿里巴巴数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.14</version>
</dependency>
### 127.0.0.1/mayikt  转到到http://www.mayikt.com/
  datasource:
    url: jdbc:mysql://localhost:3306/meite_gateWay?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

我们刷新一下,传入token,可以看到:
7.png

GateWay的词汇表有那些

路由:是网关基本的模块,分别为id(唯一)、目标uri(真实服务地址)、一组谓词+过滤器一起组合而成,如果谓词匹配成功,则路由匹配成功。
谓词: 匹配Http请求参数
过滤器:对下游的服务器之前和之后实现处理。

1、匹配时间之后

- id: mayikt
  uri: http://www.mayikt.com/
  ###匹配规则
  predicates:
    - After=2017-01-20T17:42:47.789-07:00[America/Denver]

此路由与 2017 年 1 月 20 日 17:42 MountainTime(Denver)之后的所有请求相匹配。

2、匹配对应的host

- id: meite
  uri: http://www.mayikt.com/
  ###匹配规则
  predicates:
    - Host=meite.mayikt.com

访问 mete.mayikt.com 转发到http://www.mayikt.com/

3、权重谓词

- id: weight_high
  uri: http://www.mayikt.com/yushengjun
  predicates:
    - Weight=group1, 2
- id: weight_low
  uri: http://www.mayikt.com
  predicates:
    - Weight=group1, 1

根据权重比例实现转发

- id: weight_order
  uri: lb://meitemayikt-order
  predicates:
    - Weight=group1,2
- id: weight_member
  uri: lb://mayikt-member
  predicates:
    - Weight=group1,1

详细参考:
https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories

GateWay解决跨域的问题

*/
@Component
public class CrossOriginFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
        return chain.filter(exchange);

    }
}

网关GateWay源码分析

1.客户端向网关发送Http请求,会到达DispatcherHandler接受请求,匹配到
RoutePredicateHandlerMapping。

  • 根据RoutePredicateHandlerMapping匹配到具体的路由策略。
  • FilteringWebHandler获取的路由的GatewayFilter数组,创建 GatewayFilterChain 处理过滤请求
  • 执行我们的代理业务逻辑访问。
    8.png

常用配置类说明:

  • GatewayClassPathWarningAutoConfiguration 检查是否有正确的配置webflux
  • GatewayAutoConfiguration 核心配置类
  • GatewayLoadBalancerClientAutoConfiguration 负载均衡策略处理
  • GatewayRedisAutoConfiguration Redis+lua整合限流

常见错误

Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.

原因就是
SpringCloud gateway基于webflux实现的,不是基于SpringBoot-web,所以应该删除
SpringBoot-web依赖组件。

SpringCloud gateway 常用名词

Route 路由 路由的id (唯一)、 转发uri (真实服务地址)、过滤器、谓词组成。
谓词 匹配的规则

源码分析:

SpringBoot项目源码的入口

  • GatewayClassPathWarningAutoConfiguration 作用检查是否配置我们webfux依赖。
  • GatewayAutoConfiguration加载了我们Gateway需要的注入的类。
  • GatewayLoadBalancerClientAutoConfiguration 网关需要使用的负载均衡
    Lb//mayikt-member// 根据服务名称查找真实地址
  • GatewayRedisAutoConfiguration 网关整合Redis整合Lua实现限流
  • GatewayDiscoveryClientAutoConfiguration 服务注册与发现功能

微服务中跨域的问题 不属于前端解决,jsonp 只能支持get请求。

核心点就是在我们后端。

解决跨域的问题

  • HttpClient转发
  • 使用过滤器允许接口可以跨域 响应头设置
  • Jsonp 不支持我们的post 属于前端解决
  • Nginx解决跨域的问题保持我们域名和端口号一致性
  • Nginx也是通过配置文件解决跨域的问题
  • 基于微服务网关解决跨域问题,需要保持域名和端口一致性
  • 使用网关代码允许所有的服务可以跨域的问题
  • 使用SpringBoot注解形式@CrossOrigin
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
16天前
|
算法 NoSQL API
SpringCloud&Gateway网关限流
SpringCloud&Gateway网关限流
39 7
|
1天前
|
监控 安全 Java
Spring cloud原理详解
Spring cloud原理详解
10 0
|
5天前
|
消息中间件 负载均衡 Java
【Spring Cloud 初探幽】
【Spring Cloud 初探幽】
14 1
|
7天前
|
Java 开发者 微服务
Spring Cloud原理详解
【5月更文挑战第4天】Spring Cloud是Spring生态系统中的微服务框架,包含配置管理、服务发现、断路器、API网关等工具,简化分布式系统开发。核心组件如Eureka(服务发现)、Config Server(配置中心)、Ribbon(负载均衡)、Hystrix(断路器)、Zuul(API网关)等。本文讨论了Spring Cloud的基本概念、核心组件、常见问题及解决策略,并提供代码示例,帮助开发者更好地理解和实践微服务架构。此外,还涵盖了服务通信方式、安全性、性能优化、自动化部署、服务网格和无服务器架构的融合等话题,揭示了微服务架构的未来趋势。
31 6
|
11天前
|
JSON Java Apache
Spring Cloud Feign 使用Apache的HTTP Client替换Feign原生httpclient
Spring Cloud Feign 使用Apache的HTTP Client替换Feign原生httpclient
|
11天前
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
13天前
|
消息中间件 Java RocketMQ
Spring Cloud RocketMQ:构建可靠消息驱动的微服务架构
【4月更文挑战第28天】消息队列在微服务架构中扮演着至关重要的角色,能够实现服务之间的解耦、异步通信以及数据分发。Spring Cloud RocketMQ作为Apache RocketMQ的Spring Cloud集成,为微服务架构提供了可靠的消息传输机制。
28 1
|
13天前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo: 微服务通信的高效解决方案
【4月更文挑战第28天】在微服务架构的发展中,服务间的高效通信至关重要。Spring Cloud Dubbo 提供了一种基于 RPC 的通信方式,使得服务间的调用就像本地方法调用一样简单。本篇博客将探讨 Spring Cloud Dubbo 的核心概念,并通过具体实例展示其在项目中的实战应用。
15 2
|
13天前
|
监控 Java Sentinel
Spring Cloud Sentinel:概念与实战应用
【4月更文挑战第28天】在分布式微服务架构中,确保系统的稳定性和可靠性至关重要。Spring Cloud Sentinel 为微服务提供流量控制、熔断降级和系统负载保护,有效预防服务雪崩。本篇博客深入探讨 Spring Cloud Sentinel 的核心概念,并通过实际案例展示其在项目中的应用。
23 0