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進行處理。
主要關注 pre、post和error。分別代表前置過濾,後置過濾和異常過濾。
請求先進入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.html。Google 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;
}
}