微服務(wù)網(wǎng)關(guān)zuul

1、 什么是路由網(wǎng)關(guān)

網(wǎng)關(guān)是系統(tǒng)的唯一對(duì)外的入口,介于客戶端和服務(wù)器端之間的中間層,處理非業(yè)務(wù)功能 提供路由請(qǐng)求、鑒權(quán)、監(jiān)控、緩存、限流等功能。它將"1對(duì)N"問題轉(zhuǎn)換成了"1對(duì)1”問題。
通過服務(wù)路由的功能,可以在對(duì)外提供服務(wù)時(shí),只暴露 網(wǎng)關(guān)中配置的調(diào)用地址,而調(diào)用方就不需要了解后端具體的微服務(wù)主機(jī)。

2、為什么要使用微服務(wù)網(wǎng)關(guān)

不同的微服務(wù)一般會(huì)有不同的網(wǎng)絡(luò)地址,而客戶端可能需要調(diào)用多個(gè)服務(wù)接口才能完成一個(gè)業(yè)務(wù)需求,若讓客戶端直接與各個(gè)微服務(wù)通信,會(huì)有以下問題:
(1)客戶端會(huì)多次請(qǐng)求不同微服務(wù),增加了客戶端復(fù)雜性
(2)存在跨域請(qǐng)求,處理相對(duì)復(fù)雜
(3)認(rèn)證復(fù)雜,每個(gè)服務(wù)都需要獨(dú)立認(rèn)證
(4)難以重構(gòu),多個(gè)服務(wù)可能將會(huì)合并成一個(gè)或拆分成多個(gè)

3、網(wǎng)關(guān)的優(yōu)點(diǎn)

微服務(wù)網(wǎng)關(guān)介于服務(wù)端與客戶端的中間層,所有外部服務(wù)請(qǐng)求都會(huì)先經(jīng)過微服務(wù)網(wǎng)關(guān)客戶只能跟微服務(wù)網(wǎng)關(guān)進(jìn)行交互,無(wú)需調(diào)用特定微服務(wù)接口,使得開發(fā)得到簡(jiǎn)化
總的理解網(wǎng)關(guān)優(yōu)點(diǎn)
服務(wù)網(wǎng)關(guān) = 路由轉(zhuǎn)發(fā) + 過濾器
(1)路由轉(zhuǎn)發(fā):接收一切外界請(qǐng)求,轉(zhuǎn)發(fā)到后端的微服務(wù)上去。
(2)過濾器:在服務(wù)網(wǎng)關(guān)中可以完成一系列的橫切功能,例如權(quán)限校驗(yàn)、限流以及監(jiān)控等,這些都可以通過過濾器完成(其實(shí)路由轉(zhuǎn)發(fā)也是通過過濾器實(shí)現(xiàn)的)。

4、Zuul項(xiàng)目搭建

使用到的組件包括:Eureka、Feign、Zuul,包括以下四個(gè)項(xiàng)目:
(1)Eureka-server: 8761 注冊(cè)中心
(2)product-server :8771 商品微服務(wù)
(3)order-server : 8781 訂單微服務(wù)
(4)zuul-gateway : 9000 Zuul網(wǎng)關(guān)

  • 1、加入依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yuan</groupId>
    <artifactId>api-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • 2、啟動(dòng)類加入注解
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }

}
  • 3、配置文件

server:
  port: 9000

#服務(wù)的名稱
spring:
  application:
    name: api-gateway

#指定注冊(cè)中心地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

注冊(cè)中心可以看到注冊(cè)的網(wǎng)關(guān):


注冊(cè)中心

正常訪問order-service的路徑:



通過網(wǎng)關(guān)訪問order-service的路徑:
默認(rèn)訪問規(guī)則  http://gateway:port/service-id/**
例子:默認(rèn) /order-service/api/v1/order/save?user_id=2&product_id=1

可修改配置

#自定義路由轉(zhuǎn)發(fā)
zuul:
  routes:
    order-service: /gateway-order/**
    #忽略整個(gè)服務(wù)
  #ignored-services: product-service
  #忽略一定規(guī)則的服務(wù)
  #ignored-patterns: /*-service/**

如上配置可用下方方式訪問


5、Zuul常用問題分析

1、路由名稱定義問題: 路由映射重復(fù)會(huì)覆蓋
如下 這樣定義 會(huì)覆蓋 只有product-service生效

zuul:
  routes:
    order-service: /gateway-order/**
    product-service: /gateway-order/**

2、Http請(qǐng)求頭過濾問題

通過request.getHeader("cookie");為空,原因是Zuul會(huì)默認(rèn)過濾如下三個(gè)header
修改為如下配置 可去掉
zuul:
  sensitive-headers:

5、Zuul流程

Zuul流程

6、自定義Zuul過濾器

  • 權(quán)限校驗(yàn)

1、LoginFilter類

package com.yuan.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

@Component
public class LoginFilter extends ZuulFilter {

    /**
     * 過濾器類型  前置過濾器
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 過濾器執(zhí)行順序 數(shù)字越小越先執(zhí)行
     * @return
     */
    @Override
    public int filterOrder() {
        return 4;
    }

    /**
     * 是否進(jìn)行過濾
     * @return
     */
    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String requestURI = request.getRequestURI();
        //只攔截此路徑
        if("/gateway-order/api/v1/order/saveByFeign".equalsIgnoreCase(requestURI)){
            return true;
        }
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("攔截啦");
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String token = request.getHeader("token");
        if(StringUtils.isBlank(token)){
            token=request.getParameter("token");
        }

        if(StringUtils.isBlank(token)){
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        }
        return null;
    }

2、關(guān)鍵說明
(1)方法說明
filterType : filter類型,分為pre、error、post、 route
filterOrder: filter執(zhí)行順序,通過數(shù)字指定,數(shù)字越小,執(zhí)行順序越先
shouldFilter: filter是否需要執(zhí)行 true執(zhí)行 false 不執(zhí)行
run : filter具體邏輯(上面為true那么這里就是具體執(zhí)行邏輯)

(2)filter類型說明
pre: 請(qǐng)求執(zhí)行之前filter
route: 處理請(qǐng)求,進(jìn)行路由
post: 請(qǐng)求處理完成后執(zhí)行的filter
error: 出現(xiàn)錯(cuò)誤時(shí)執(zhí)行的filter

3、測(cè)試
(1)該路徑訪問被攔截



(2)該路徑可以正常訪問


  • 接口限流

接口限流可以在nginx層面做限流,也可以在網(wǎng)關(guān)層面做限流,這里在網(wǎng)關(guān)層面做限流,基于guava框架來(lái)做網(wǎng)關(guān)限流。先對(duì)guava框架限流的概念進(jìn)行講解下

它的大致意思就是每一個(gè)請(qǐng)求進(jìn)來(lái)先到桶里去拿令牌,拿到令牌的請(qǐng)求放行,假設(shè)你設(shè)置了1000個(gè)令牌,如果拿完了,那么后面來(lái)調(diào)接口的請(qǐng)求就需要排隊(duì)等有新的令牌才能調(diào)用該接口。

package com.yuan.apigateway.filter;


import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;


public class OrderRateLimiterFilter extends ZuulFilter {

    //每秒產(chǎn)生1000個(gè)令牌
    private static RateLimiter RATE_LIMTER=RateLimiter.create(1000);

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return -4;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        //只針對(duì)此接口限流
        if("/gateway-order/api/v1/order/saveByFeign".equalsIgnoreCase(request.getRequestURI())){
            return true;
        }
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        //就相當(dāng)于每調(diào)用一次tryAcquire()方法,令牌數(shù)量減1,當(dāng)1000個(gè)用完后,那么后面進(jìn)來(lái)的用戶無(wú)法訪問上面接口
        //當(dāng)然這里只寫類上面一個(gè)接口,可以這么寫,實(shí)際可以在這里要加一層接口判斷。
        if(!RATE_LIMTER.tryAcquire()){
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(HttpStatus.SC_REQUEST_TOO_LONG);
        }
        return null;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容