天天看點

如何動态請求參數網關? | 帶你讀《Spring Cloud Alibaba(2019)》之十二

上一篇:如何保證微服務接口的安全? | 帶你讀《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,可以看到:

如何動态請求參數網關? | 帶你讀《Spring Cloud Alibaba(2019)》之十二

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 處理過濾請求
  • 執行我們的代理業務邏輯通路。
    如何動态請求參數網關? | 帶你讀《Spring Cloud Alibaba(2019)》之十二

常用配置類說明:

  • 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