現有微服務的兩點不足:
-
對於在微服務體系中、和EurekaServer通訊的微服務來講,使用服務名即可訪問。但是對於手機、web端等外部訪問者仍然需要和N多服務器交互,需要記憶他們的服務器地址、端口號等。一旦內部發生修改,很麻煩,而且有時候內部服務器是不希望外界直接訪問的。
-
各個業務系統的人無法自由的維護自己負責的服務器;
-
現有的微服務都是“我家大門常打開”,沒有做權限校驗。如果把權限校驗代碼寫到每個微服務上,那麼開發工作量太大。
zuul就是一個提供了請求路由、驗證登錄等功能的API網關微服務。
一、搭建Zuul服務
a)新建一個Spring項目,勾選Zuul、EurekaDiscovery。
b)***Application使用@EnableZuulProxy開啓Zuul功能。
c)application.properties中加入
server.port=8888
spring.application.name=apigate
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8080/eureka/
二、請求路由
在配置中加入zuul.routes.<服務名>=請求路徑這樣的格式。比如zuul.routes.restservice1=/restservice1/** 當用戶請求http://127.0.0.1:8888/restservice1/person/getById?id=1的時候就會訪問restservice1服務的person/getById?id=1。
如果給web項目設置spring.application.name=testweb2,那麼如果如下配置
zuul.routes.testweb2=/web/**
那麼當訪問http://127.0.0.1:8888/web/test/test1的時候就會訪問testweb2項目的/test/test1
我們一般管restservice1叫zuul的“上游(upstream)服務器”。
注意:如果在“上游服務器”接口服務上設置了斷點,並且斷的時間稍微長一點,就會導致zuul這邊收到超時錯誤。
這樣對於外部訪問者只要和http://127.0.0.1:8888打交道就行了,不需要直接和具體某個服務通訊。
網站內部如何通過Eureka實現自動擴容,對外再借助於Zuul實現前端服務器的自動擴容。這是微服務架構牛逼的地方。
不建議用zuul做純web站的網關,zuul只做接口服務的網關。web站的網關建議用nginx。
三、請求過濾
支持ZuulFilter,可以在請求被路由之前進行權限校驗等。
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
public class CheckFilter extends ZuulFilter {
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token");
if(token==null||!token.equals("123"))//請求中有token=123就認爲認證通過
{
System.out.println("token錯誤");
ctx.setSendZuulResponse(false);//不向上游服務器轉發請求了
ctx.setResponseStatusCode(401);//給下游調用者返回的錯誤碼
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public String filterType() {
return "pre";
}
}
在***Application中加入
@Bean
CheckFilter filter1()
{
return new CheckFilter();
}
注意Zuul服務器和“上游服務器”是運行在兩個進程甚至兩臺服務器上,因此在Zuul中requeset.setAttribute中設置的值在“上游服務器”中獲取不到。在ZuulFilter中可以通過下面的方式向“上游服務器”傳遞報文頭:
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
ctx.addZuulRequestHeader(“X-MyHeader1”, “hello”);
在“上游服務器”中的請求頭中就會有"X-MyHeader1"值。
ZuulFilter其他filterType:pre(請求被路由之前調用);routing(路由請求時被調用);error:發生錯誤時候調用;post:在routing和error過濾器後調用;
可以編寫postFilter在給上游服務器發過來的響應發給下游請求者之前進行添加報文頭等的處理:
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
public class PostFilter extends ZuulFilter {
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulResponseHeader("X-Shit", "6666");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public String filterType() {
return "post";
}
}