微服務網關實戰04-網關限流及斷路

在上一章節中,我們做了網關的日誌請求及全局異常,本章節我們將會介紹限流及斷路。

對於很多同學來說,限流跟斷路應該都是經常遇見的,但是有很多新同學並不知道,這裏先介紹一下限流跟斷路。

限流:限制流量,舉個例子,我提供了一個接口出去,限定一秒內訪問的次數只能是100次,這就是限流。本文采用谷歌的RateLimiter來實現

斷路:斷路也叫斷路器,當一個請求一直在請求中,超過一定時間後,就會觸發斷路器,直接給請求返回一個異常,告知例如請求超時之類的提示。本文的斷路器採用hystrix來實現,斷路器面板採用dashboard

微服務網關實戰04-網關限流及斷路

 

整體的項目結構如下:新增了兩個類,斷路器攔截,斷路器配置類,限流器

微服務網關實戰04-網關限流及斷路

項目結構圖

接下里我們直接開幹,首先是pom文件引入,增加以下包

    <!-- 斷路器 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency>

		<!-- 斷路器面板 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
		</dependency>

新增斷路器類FallbackFilter

/**
 * All rights Reserved, Designed By OprCalf
 * Copyright:    Copyright(C) 2016-2020
 * Company       LengYin Ltd.
 */
package com.platform.gateway.common.filter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import com.platform.gateway.common.utils.MsgUtils;

/**
 * @projectName:  platform-gateway-demo
 * @package:      com.platform.gateway.common.filters
 * @className:    FallbackFilter.java
 * @description:  路由斷路器
 * @author:       OprCalf
 * @date:         2020年3月4日
 */
@Component
public class FallbackFilter implements FallbackProvider {

    @Override
    public String getRoute() {
        // 表明爲哪個微服務提供回退,return "*"代表爲所有微服務提供回退
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                // 獲取狀態碼(200,OK)
                return HttpStatus.BAD_GATEWAY;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                // 返回數字狀態碼
                return HttpStatus.BAD_GATEWAY.value();
            }

            @Override
            public String getStatusText() throws IOException {
                // 返回字母狀態碼
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                // 返回的內容
                return new ByteArrayInputStream(MsgUtils.buildFailureMsg("找不到服務,請稍後再試").toString().getBytes("utf-8"));
            }

            @SuppressWarnings("deprecation")
            @Override
            public HttpHeaders getHeaders() {
                final HttpHeaders headers = new HttpHeaders();
                // 返回時的Header體的設置
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }

}

新增限流器類LimitFilter

這裏限制每秒只能有100個請求,超過的話,會自動提示錯誤信息

/**
 * All rights Reserved, Designed By OprCalf
 * Copyright:    Copyright(C) 2016-2020
 * Company       LengYin Ltd.
 */

package com.platform.gateway.common.filter;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

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 com.platform.gateway.common.utils.MsgUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @projectName:  platform-gateway-demo
 * @package:      com.platform.gateway.common.filters
 * @className:    LimitFilter.java
 * @description:  限流過濾
 * @author:       OprCalf
 * @date:         2020年3月4日
 */
@Component
@Slf4j
public class LimitFilter extends ZuulFilter {

    // 每秒產生100個令牌
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);

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

    @Override
    public int filterOrder() {
        return Integer.MIN_VALUE + 2;
    }

    @Override
    public boolean shouldFilter() {
        final RequestContext ctx = RequestContext.getCurrentContext();
        // 當發生異常的時候,不是進行取值
        if (ctx.getThrowable() != null) {
            log.error("{}", ctx.getThrowable().fillInStackTrace());
            if (ctx.sendZuulResponse()) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    @Override
    public Object run() throws ZuulException {
        final RequestContext requestContext = RequestContext.getCurrentContext();
        // 就相當於每調用一次tryAcquire()方法,令牌數量減1,當1000個用完後,那麼後面進來的用戶無法訪問上面接口
        // 當然這裏只寫類上面一個接口,可以這麼寫,實際可以在這裏要加一層接口判斷。
        if (!RATE_LIMITER.tryAcquire()) {
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
            final HttpServletResponse response = requestContext.getResponse();
            response.setContentType("application/json;charset=UTF-8");
            try {
                response.getOutputStream()
                        .write(MsgUtils.buildFailureMsg("訪問量巨大,稍後再試").toString().getBytes("utf-8"));
            }
            catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

新增HystrixConfig斷路器的面板類

/**
 * All rights Reserved, Designed By OprCalf
 * Copyright:    Copyright(C) 2016-2020
 * Company       LengYin Ltd.
 */

package com.platform.gateway.common.config;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;

/**
 * @projectName:  platform-gateway-demo
 * @package:      com.platform.gateway.common.configuration
 * @className:    HystrixConfiguration.java
 * @description:  斷路器信息控制面板數據
 * @author:       OprCalf
 * @date:         2020年3月5日
 */
@Configuration
public class HystrixConfig {

    /**
     * 普通斷路器信息
     * @author OprCalf
     * @return ServletRegistrationBean<HystrixMetricsStreamServlet>
     */
    @Bean(name = "hystrixRegistrationBean")
    public ServletRegistrationBean<HystrixMetricsStreamServlet> servletRegistrationBean() {
        final ServletRegistrationBean<HystrixMetricsStreamServlet> registration = new ServletRegistrationBean<>(
                new HystrixMetricsStreamServlet(), "/hystrix.stream");
        registration.setName("hystrixServlet");
        registration.setLoadOnStartup(1);
        return registration;
    }

    /**
     * 給turbine使用的斷路器信息,跟上面一樣的
     * @author OprCalf
     * @return ServletRegistrationBean<HystrixMetricsStreamServlet>
     */
    @Bean(name = "hystrixForTurbineRegistrationBean")
    public ServletRegistrationBean<HystrixMetricsStreamServlet> servletTurbineRegistrationBean() {
        final ServletRegistrationBean<HystrixMetricsStreamServlet> registration = new ServletRegistrationBean<>(
                new HystrixMetricsStreamServlet(), "/actuator/hystrix.stream");
        registration.setName("hystrixForTurbineServlet");
        registration.setLoadOnStartup(1);
        return registration;
    }

}

在啓動類
PlatformGatewayApplication新增註解@EnableHystrixDashboard

這個註解是啓動斷路器的監控面板,待會我們可以看到斷路器的工作情況。

最後,我們在配置文件application.yml上,添加斷路器及監控的一些信息

######健康監控配置###################################
#開放所有頁面節點  默認只開啓了health、info兩個節點
management.endpoints.web.exposure.include: "hystrix.stream,health,info,routes"
#顯示健康具體信息  默認不會顯示詳細信息  
management.endpoint.health.show-details: always
######斷路器設置###################################
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 3000
#設置Hystrix隔離策略爲線程池
zuul.ribbonIsolationStrategy: thread
#每個路由使用獨立的線程池
zuul.threadPool.UseSeparateThreadPools: true

至此,項目完成,我們來繼續我們的實驗。

限流測試:啓用200個線程來請求,如果沒效果,調成300

微服務網關實戰04-網關限流及斷路

限流測試

從圖中可以看到,當超過一定限制的時候,會直接返回我們的“訪問量巨大”的提示,證明限流器起作用了。

斷路測試:把後端的原子服務停掉

微服務網關實戰04-網關限流及斷路

斷路測試

從圖片中,能直接看到,後端服務停掉的時候,直接返回“找不到服務”的信息。斷路器起作用了。

斷路器監控:訪問localhost:8012/hystrix,並輸入
localhost:8012/hystrix.stream

微服務網關實戰04-網關限流及斷路

斷路器監控面板

微服務網關實戰04-網關限流及斷路

服務斷路面板

這樣就可以直接看到服務的調用情況。

備註:實際中這個很少用到,一般只用到他的接口做定製化的斷路監控,畢竟,太醜了些。

最後,我們做個總結:zuul的斷路及限流,限流這裏可以結合redis來做,但是如果是小項目的話,並且不需要對網關做高可用的話,直接用谷歌的限流器來做也行的,要因地制宜,切記不要生搬硬套,不要人云亦云。

最後,謝謝觀賞,覺得好的話,點個贊,有什麼問題可以留言溝通,麼麼噠。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章