####服務閘道器簡介 服務閘道器是微服務架構中一個不可或缺的部分。通過服務閘道器統一向外系統提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它還具備了許可權控制等功能。Spring Cloud Netflix中的Zuul就擔任了這樣的一個角色,為微服務架構提供了前門保護的作用,同時將許可權控制這些較重的非業務邏輯內容遷移到服務路由層面,使得服務叢集主體能夠具備更高的可複用性和可測試性。
###依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
複製程式碼
配置
spring.application.name=api-gateway
server.port=5555
#通過url的方式做服務轉發(不推薦)
#zuul.routes.api-a-url.path=/api-a-url/**
#zuul.routes.api-a-url.url=http://localhost:2222/
#通過serviceId的方式
#api-a api-b只是自定義的名稱
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=compute-service-a
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=compute-service-b
#註冊為服務
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
複製程式碼
所有的/api-a/**
路徑的訪問轉發到compute-service-a服務
所有的/api-b/**
路徑的訪問轉發到compute-service-b服務
服務閘道器實現轉發有兩種方式,一種是通過url的方式轉發,另一種是通過serviceId的方式轉發。通過url轉發的方式,則不需要eureka依賴。
推薦使用serviceId的對映方式,除了對Zuul維護上更加友好之外,serviceId對映方式還支援了斷路器,對於服務故障的情況下,可以有效的防止故障蔓延到服務閘道器上而影響整個系統的對外服務
Application.java
@EnableZuulProxy
@SpringCloudApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
@Bean
public AccessFilter accessFilter(){
return new AccessFilter();
}
}
複製程式碼
自定義過濾器AccessFilter.java
public class AccessFilter extends ZuulFilter{
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
String accessToken = request.getParameter("accessToken");
if(accessToken==null){
log.warn("access token is empty");
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
currentContext.setResponseBody("accessToken is null");
return null;
}
log.info("access token ok");
return null;
}
}
複製程式碼
自定義過濾器的實現,需要繼承ZuulFilter,需要重寫實現下面四個方法:
-
filterType:返回一個字串代表過濾器的型別,在zuul中定義了四種不同生命週期的過濾器型別,具體如下:
- pre:可以在請求被路由之前呼叫
- routing:在路由請求時候被呼叫
- post:在routing和error過濾器之後被呼叫
- error:處理請求時發生錯誤時被呼叫
-
filterOrder:通過int值來定義過濾器的執行順序
-
shouldFilter:返回一個boolean型別來判斷該過濾器是否要執行,所以通過此函式可實現過濾器的開關。在上例中,我們直接返回true,所以該過濾器總是生效。
-
run:過濾器的具體邏輯。需要注意,這裡我們通過ctx.setSendZuulResponse(false)令zuul過濾該請求,不對其進行路由,然後通過ctx.setResponseStatusCode(401)設定了其返回的錯誤碼,當然我們也可以進一步優化我們的返回,比如,通過ctx.setResponseBody(body)對返回body內容進行編輯等。
將服務閘道器注冊為服務,將許可權系統做成另外一個服務,在閘道器裡面呼叫這個服務,來實現許可權控制。