Spring Cloud學習筆記【十一】微服務網關Zuul的過濾和限流

                        Spring Cloud學習筆記【十一】微服務網關Zuul的過濾和限流

zuul的工作原理

zuul的核心是一系列的filters, 其作用可以類比Servlet框架的Filter,或者AOP。這些過濾器幫助我們執行以下功能:

  • 身份驗證和安全性——識別每個資源的身份驗證需求並拒絕不滿足這些需求的請求。
  • 洞察和監控——在邊緣跟蹤有意義的數據和統計數據,以便爲我們提供準確的生產視圖。
  • 動態路由——根據需要動態地將請求路由到不同的後端集羣。
  • 壓力測試——逐步增加集羣的流量,以評估性能。
  • 減少負載——爲每種類型的請求分配容量,並刪除超過限制的請求。
  • 靜態響應處理——直接在邊緣構建一些響應,而不是將它們轉發到內部集羣。
  • 多區域彈性——跨AWS區域路由請求,以使我們的ELB使用多樣化,並使我們的優勢更接近我們的成員。

a.依賴引入

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

b.@EnableZuulProxy註解添加

@EnableEurekaClient
@EnableZuulProxy
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

d.配置文件配置    

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: zull
server:
  port: 9000

c.編寫測試demo

@RestController
@RequestMapping("/order")
public class OrderInfoController {
    @Autowired
    private OrderCodeConfig orderCodeConfig;
    @Autowired
    private OrderInfoService orderInfoService;
    /**
     * @todo 查詢訂單列表
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/list",method = RequestMethod.GET)
    public String list() throws Exception {
        List<OrderInfo> list = orderInfoService.list();
        return JSON.toJSONString(list);
    }
    /**
     * @todo 查詢訂單配置
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/config",method = RequestMethod.GET)
    public String config() throws Exception {
        return JSON.toJSONString(orderCodeConfig);
    }
}

e.自定義過濾器

@Component
public class TokenFilter extends ZuulFilter {

    private Log LOGGER = LogFactory.getLog(TokenFilter.class);

    /**
     * 過濾器類型:有四種類型(分別爲:ERROR_TYPE、POST_TYPE、PRE_TYPE、ROUTE_TYPE)
     *
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 過濾的順序:越小越靠前,FilterConstants.PRE_DECORATION_FILTER_ORDER已經定義了的大小順序,可以直接使用
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    /**
     * 過濾是否生效:代表這個過濾器是否生效(true 、 false)
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 處理業務代碼的邏輯
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        LOGGER.info("進入token的過濾器");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");//獲取請求的參數
        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true);  //對該請求進行路由
            ctx.setResponseStatusCode(HttpStatus.OK.value());
            ctx.set("isSuccess", true); // 設值,讓下一個Filter看到上一個Filter的狀態
        } else {
            ctx.setSendZuulResponse(false);  //過濾該請求,不對其進行路由
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());// 返回錯誤碼
            ctx.setResponseBody("this is not auth"); //返回錯誤信息
            ctx.set("isSuccess", false);
        }
        ctx.getResponse().setContentType("text/html;charset=UTF-8");
        return null;
    }

}

啓動測試:http://localhost:9000/myOrder/order/list,不進行token攜帶

http://localhost:9000/myOrder/order/list?token=abc,進行token攜帶

 

直接查看這個類,定義了過濾器的一些常用的信息:

org.springframework.cloud.netflix.zuul.filters.support.FilterConstants

1、filterType過濾器類型(一共定義了四種)

PRE:該類型的filters在Request routing到源web-service之前執行。用來實現Authentication、選擇源服務地址等

ROUTING:該類型的filters用於把Request routing到源web-service,源web-service是實現業務邏輯的服務。這裏使用HttpClient請求web-service。

POST:該類型的filters在ROUTING返回Response後執行。用來實現對Response結果進行修改,收集統計數據以及把Response傳輸會客戶端。

ERROR:上面三個過程中任何一個出現錯誤都交由ERROR類型的filters進行處理。

主要關注 preposterror。分別代表前置過濾,後置過濾和異常過濾。

請求先進入pre的filter類,你可以進行一些權限認證,日誌記錄,或者額外給Request增加一些屬性供後續的filter使用。pre會優先按照order從小到大執行,然後再去執行請求轉發到業務服務。

post,那麼就會執行完被路由的業務服務後,再進入post的filter,在post的filter裏,一般做一些日誌記錄,或者額外增加response屬性什麼的。

error,如果在上面的任何一個地方出現了異常,就會進入到type爲error的filter中,進行異常的統一處理。

2、filterOrder過濾器順序(數字越大,優先級越低)

3、shouldFilter過濾器是否生效(true/false)

4、Run方法(處理邏輯的地方)

  下圖是filter的執行順序

過濾器間的協調
    過濾器沒有直接的方式來訪問對方。 它們可以使用RequestContext共享狀態,這是一個類似Map的結構,具有一些顯式訪問器方法用於被認爲是Zuul的原語,內部是使用ThreadLocal實現的。

禁用Zuul過濾器

    設置zuul.<SimpleClassName>.<filterType>.disable=true ,即可禁用SimpleClassName所對應的過濾器。比如:自定義的過濾器com.example.demo.filter.TokenFilter進行禁用,可以這樣設置:

zuul:
  TokenFilter:
    pre:
      disable: true

限流

Zuul 上實現限流是個不錯的選擇,只需要編寫一個過濾器就可以了,關鍵在於如何實現限流的算法。限流算法有漏桶算法以及令牌桶算法,可參考 https://www.cnblogs.com/LBSer/p/4083131.htmlGoogle Guava 爲我們提供了限流工具類RateLimiter。

@Component
public class RateLimitZuulFilter extends ZuulFilter {

    private final RateLimiter rateLimiter = RateLimiter.create(1000.0);

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

    @Override
    public int filterOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public boolean shouldFilter() {
        // 這裏可以考慮弄個限流開啓的開關,開啓限流返回true,關閉限流返回false,你懂的。
        return true;
    }

    @Override
    public Object run() {
        try {
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletResponse response = currentContext.getResponse();
            if (!rateLimiter.tryAcquire()) {
                HttpStatus httpStatus = HttpStatus.TOO_MANY_REQUESTS;

                response.setContentType(MediaType.TEXT_PLAIN_VALUE);
                response.setStatus(httpStatus.value());
                response.getWriter().append(httpStatus.getReasonPhrase());

                currentContext.setSendZuulResponse(false);

                throw new ZuulException(
                        httpStatus.getReasonPhrase(),
                        httpStatus.value(),
                        httpStatus.getReasonPhrase()
                );
            }
        } catch (Exception e) {
            ReflectionUtils.rethrowRuntimeException(e);
        }
        return null;
    }
}

這個是單點的限流措施,對於分佈式和微服務可以參考:http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/

跨域

@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

 

 

 

 

 

發佈了71 篇原創文章 · 獲贊 32 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章