通過Zuul上傳文件

如果您@EnableZuulProxy您可以使用代理路徑上傳文件接谨,只要文件很小荆残,它就應(yīng)該工作。對于大文件橄妆,有一個替代路徑繞過“/ zuul / *”中的SpringDispatcherServlet(以避免多部分處理)。也就是說祈坠,如果zuul.routes.customers=/customers/**則可以將大文件發(fā)送到“/ zuul / customers / *”害碾。servlet路徑通過zuul.servletPath進(jìn)行外部化。如果代理路由引導(dǎo)您通過Ribbon負(fù)載均衡器赦拘,例如慌随,超大文件也將需要提升超時設(shè)置

application.yml

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000

ribbon:

? ConnectTimeout: 3000

? ReadTimeout: 60000

請注意,要使用大型文件進(jìn)行流式傳輸躺同,您需要在請求中使用分塊編碼(某些瀏覽器默認(rèn)情況下不會執(zhí)行)阁猜。例如在命令行:

$ curl -v -H "Transfer-Encoding: chunked" \

? ? -F "file=@mylarge.iso" localhost:9999/zuul/simple/file

查詢字符串編碼

處理傳入的請求時,查詢參數(shù)被解碼蹋艺,因此可以在Zuul過濾器中進(jìn)行修改剃袍。然后在路由過濾器中構(gòu)建后端請求時重新編碼它們。如果使用Javascript的encodeURIComponent()方法編碼捎谨,結(jié)果可能與原始輸入不同民效。雖然這在大多數(shù)情況下不會出現(xiàn)任何問題,但一些Web服務(wù)器可以用復(fù)雜查詢字符串的編碼來挑選涛救。

要強(qiáng)制查詢字符串的原始編碼畏邢,可以將特殊標(biāo)志傳遞給ZuulProperties,以便查詢字符串與HttpServletRequest::getQueryString方法相同:

application.yml

zuul:

? forceOriginalQueryStringEncoding: true

注意:此特殊標(biāo)志僅適用于SimpleHostRoutingFilter州叠,您可以使用RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)輕松覆蓋查詢參數(shù)棵红,因?yàn)椴樵冏址F(xiàn)在直接在原始的HttpServletRequest上獲取。

普通嵌入Zuul

如果您使用@EnableZuulServer(而不是@EnableZuulProxy)咧栗,您也可以運(yùn)行不帶代理的Zuul服務(wù)器逆甜,或者有選擇地切換代理平臺的部分虱肄。您添加到ZuulFilter類型的應(yīng)用程序的任何bean都將自動安裝,與@EnableZuulProxy一樣交煞,但不會自動添加任何代理過濾器咏窿。

在這種情況下,仍然通過配置“zuul.routes素征。*”來指定進(jìn)入Zuul服務(wù)器的路由集嵌,但沒有服務(wù)發(fā)現(xiàn)和代理,所以“serviceId”和“url”設(shè)置將被忽略御毅。例如:

application.yml

zuul:

? routes:

? ? api: /api/**

將“/ api / **”中的所有路徑映射到Zuul過濾器鏈根欧。

禁用Zuul過濾器

Spring Cloud的Zuul在代理和服務(wù)器模式下默認(rèn)啟用了多個ZuulFilterbean。有關(guān)啟用的可能過濾器端蛆,請參閱zuul過濾器包凤粗。如果要禁用它,只需設(shè)置zuul.<SimpleClassName>.<filterType>.disable=true今豆。按照慣例嫌拣,filters之后的包是Zuul過濾器類型。例如呆躲,禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter設(shè)置zuul.SendResponseFilter.post.disable=true异逐。

為路線提供Hystrix回退

當(dāng)Zuul中給定路由的電路跳閘時,您可以通過創(chuàng)建類型為ZuulFallbackProvider的bean來提供回退響應(yīng)插掂。在這個bean中灰瞻,您需要指定回退的路由ID,并提供返回的ClientHttpResponse作為后備燥筷。這是一個非常簡單的ZuulFallbackProvider實(shí)現(xiàn)箩祥。

class MyFallbackProvider implements ZuulFallbackProvider {

? ? @Override

? ? public String getRoute() {

? ? ? ? return "customers";

? ? }

? ? @Override

? ? public ClientHttpResponse fallbackResponse() {

? ? ? ? return new ClientHttpResponse() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public HttpStatus getStatusCode() throws IOException {

? ? ? ? ? ? ? ? return HttpStatus.OK;

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public int getRawStatusCode() throws IOException {

? ? ? ? ? ? ? ? return 200;

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public String getStatusText() throws IOException {

? ? ? ? ? ? ? ? return "OK";

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public void close() {

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public InputStream getBody() throws IOException {

? ? ? ? ? ? ? ? return new ByteArrayInputStream("fallback".getBytes());

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public HttpHeaders getHeaders() {

? ? ? ? ? ? ? ? HttpHeaders headers = new HttpHeaders();

? ? ? ? ? ? ? ? headers.setContentType(MediaType.APPLICATION_JSON);

? ? ? ? ? ? ? ? return headers;

? ? ? ? ? ? }

? ? ? ? };

? ? }

}

這里是路由配置的樣子。

zuul:

? routes:

? ? customers: /customers/**

如果您希望為所有路由提供默認(rèn)的回退肆氓,您可以創(chuàng)建一個類型為ZuulFallbackProvider的bean,并且getRoute方法返回*或null底瓣。

class MyFallbackProvider implements ZuulFallbackProvider {

? ? @Override

? ? public String getRoute() {

? ? ? ? return "*";

? ? }

? ? @Override

? ? public ClientHttpResponse fallbackResponse() {

? ? ? ? return new ClientHttpResponse() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public HttpStatus getStatusCode() throws IOException {

? ? ? ? ? ? ? ? return HttpStatus.OK;

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public int getRawStatusCode() throws IOException {

? ? ? ? ? ? ? ? return 200;

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public String getStatusText() throws IOException {

? ? ? ? ? ? ? ? return "OK";

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public void close() {

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public InputStream getBody() throws IOException {

? ? ? ? ? ? ? ? return new ByteArrayInputStream("fallback".getBytes());

? ? ? ? ? ? }

? ? ? ? ? ? @Override

? ? ? ? ? ? public HttpHeaders getHeaders() {

? ? ? ? ? ? ? ? HttpHeaders headers = new HttpHeaders();

? ? ? ? ? ? ? ? headers.setContentType(MediaType.APPLICATION_JSON);

? ? ? ? ? ? ? ? return headers;

? ? ? ? ? ? }

? ? ? ? };

? ? }

}

Zuul開發(fā)人員指南

有關(guān)Zuul如何工作的一般概述谢揪,請參閱Zuul維基

Zuul Servlet

Zuul被實(shí)現(xiàn)為Servlet捐凭。對于一般情況拨扶,Zuul嵌入到Spring調(diào)度機(jī)制中。這允許Spring MVC控制路由茁肠。在這種情況下患民,Zuul被配置為緩沖請求。如果需要通過Zuul不緩沖請求(例如大文件上傳)垦梆,Servlet也將安裝在Spring調(diào)度程序之外匹颤。默認(rèn)情況下仅孩,它位于/zuul∮”停可以使用zuul.servlet-path屬性更改此路徑辽慕。

Zuul RequestContext

要在過濾器之間傳遞信息,Zuul使用a RequestContext赦肃。其數(shù)據(jù)按照每個請求的ThreadLocal進(jìn)行溅蛉。關(guān)于路由請求,錯誤以及實(shí)際HttpServletRequest和HttpServletResponse的路由信息??他宛。RequestContext擴(kuò)展ConcurrentHashMap船侧,所以任何東西都可以存儲在上下文中。FilterConstants包含由Spring Cloud Netflix安裝的過濾器使用的密鑰(稍后再安裝)厅各。

@EnableZuulProxy與@EnableZuulServer

Spring Cloud Netflix根據(jù)使用何種注釋來啟用Zuul安裝多個過濾器勺爱。@EnableZuulProxy是@EnableZuulServer的超集。換句話說讯检,@EnableZuulProxy包含@EnableZuulServer安裝的所有過濾器琐鲁。“代理”中的其他過濾器啟用路由功能人灼。如果你想要一個“空白”Zuul围段,你應(yīng)該使用@EnableZuulServer。

@EnableZuulServer過濾器

創(chuàng)建從Spring Boot配置文件加載路由定義的SimpleRouteLocator投放。

安裝了以下過濾器(正常Spring豆類):

前置過濾器

ServletDetectionFilter:檢測請求是否通過Spring調(diào)度程序奈泪。使用鍵FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY設(shè)置布爾值。

FormBodyWrapperFilter:解析表單數(shù)據(jù)灸芳,并對下游請求進(jìn)行重新編碼涝桅。

DebugFilter:如果設(shè)置debug請求參數(shù),則此過濾器將RequestContext.setDebugRouting()和RequestContext.setDebugRequest()設(shè)置為true烙样。

路由過濾器

SendForwardFilter:此過濾器使用ServletRequestDispatcher轉(zhuǎn)發(fā)請求冯遂。轉(zhuǎn)發(fā)位置存儲在RequestContext屬性FilterConstants.FORWARD_TO_KEY中。這對于轉(zhuǎn)發(fā)到當(dāng)前應(yīng)用程序中的端點(diǎn)很有用谒获。

過濾器:

SendResponseFilter:將代理請求的響應(yīng)寫入當(dāng)前響應(yīng)蛤肌。

錯誤過濾器:

SendErrorFilter:如果RequestContext.getThrowable()不為null,則轉(zhuǎn)發(fā)到/錯誤(默認(rèn)情況下)批狱÷阕迹可以通過設(shè)置error.path屬性來更改默認(rèn)轉(zhuǎn)發(fā)路徑(/error)。

@EnableZuulProxy過濾器

創(chuàng)建從DiscoveryClient(如Eureka)以及屬性加載路由定義的DiscoveryClientRouteLocator赔硫。每個serviceId從DiscoveryClient創(chuàng)建路由炒俱。隨著新服務(wù)的添加,路由將被刷新。

除了上述過濾器之外权悟,還安裝了以下過濾器(正常Spring豆類):

前置過濾器

PreDecorationFilter:此過濾器根據(jù)提供的RouteLocator確定在哪里和如何路由砸王。它還為下游請求設(shè)置各種與代理相關(guān)的頭。

路由過濾器

RibbonRoutingFilter:此過濾器使用Ribbon僵芹,Hystrix和可插拔HTTP客戶端發(fā)送請求处硬。服務(wù)ID位于RequestContext屬性FilterConstants.SERVICE_ID_KEY中。此過濾器可以使用不同的HTTP客戶端拇派。他們是:

ApacheHttpClient荷辕。這是默認(rèn)的客戶端。

SquareupOkHttpClientv3件豌。通過在類路徑上設(shè)置com.squareup.okhttp3:okhttp庫并設(shè)置ribbon.okhttp.enabled=true來啟用此功能疮方。

Netflix Ribbon HTTP客戶端。這可以通過設(shè)置ribbon.restclient.enabled=true來啟用茧彤。這個客戶端有限制骡显,比如它不支持PATCH方法,還有內(nèi)置的重試曾掂。

SimpleHostRoutingFilter:此過濾器通過Apache HttpClient發(fā)送請求到預(yù)定的URL惫谤。URL位于RequestContext.getRouteHost()。

自定義Zuul過濾示例

以下大部分以下“如何撰寫”示例都包含示例Zuul過濾器項(xiàng)目珠洗。還有一些操作該存儲庫中的請求或響應(yīng)正文的例子溜歪。

如何編寫預(yù)過濾器

前置過濾器用于設(shè)置RequestContext中的數(shù)據(jù),用于下游的過濾器许蓖。主要用例是設(shè)置路由過濾器所需的信息蝴猪。

public class QueryParamPreFilter extends ZuulFilter {

@Override

public int filterOrder() {

return PRE_DECORATION_FILTER_ORDER - 1; // run before PreDecoration

}

@Override

public String filterType() {

return PRE_TYPE;

}

@Override

public boolean shouldFilter() {

RequestContext ctx = RequestContext.getCurrentContext();

return !ctx.containsKey(FORWARD_TO_KEY) // a filter has already forwarded

&& !ctx.containsKey(SERVICE_ID_KEY); // a filter has already determined serviceId

}

? ? @Override

? ? public Object run() {

? ? ? ? RequestContext ctx = RequestContext.getCurrentContext();

HttpServletRequest request = ctx.getRequest();

if (request.getParameter("foo") != null) {

? ? // put the serviceId in `RequestContext`

? ? ctx.put(SERVICE_ID_KEY, request.getParameter("foo"));

? ? }

? ? ? ? return null;

? ? }

}

上面的過濾器從foo請求參數(shù)填充SERVICE_ID_KEY。實(shí)際上膊爪,做這種直接映射并不是一個好主意自阱,而是從foo的值來查看服務(wù)ID。

現(xiàn)在填寫SERVICE_ID_KEY米酬,PreDecorationFilter將不會運(yùn)行沛豌,RibbonRoutingFilter將會。如果您想要路由到完整的網(wǎng)址淮逻,請改用ctx.setRouteHost(url)琼懊。

要修改路由過濾器將轉(zhuǎn)發(fā)的路徑,請?jiān)O(shè)置REQUEST_URI_KEY爬早。

如何編寫路由過濾器

路由過濾器在預(yù)過濾器之后運(yùn)行,并用于向其他服務(wù)發(fā)出請求启妹。這里的大部分工作是將請求和響應(yīng)數(shù)據(jù)轉(zhuǎn)換到客戶端所需的模型筛严。

public class OkHttpRoutingFilter extends ZuulFilter {

@Autowired

private ProxyRequestHelper helper;

@Override

public String filterType() {

return ROUTE_TYPE;

}

@Override

public int filterOrder() {

return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1;

}

@Override

public boolean shouldFilter() {

return RequestContext.getCurrentContext().getRouteHost() != null

&& RequestContext.getCurrentContext().sendZuulResponse();

}

? ? @Override

? ? public Object run() {

OkHttpClient httpClient = new OkHttpClient.Builder()

// customize

.build();

RequestContext context = RequestContext.getCurrentContext();

HttpServletRequest request = context.getRequest();

String method = request.getMethod();

String uri = this.helper.buildZuulRequestURI(request);

Headers.Builder headers = new Headers.Builder();

Enumeration<String> headerNames = request.getHeaderNames();

while (headerNames.hasMoreElements()) {

String name = headerNames.nextElement();

Enumeration<String> values = request.getHeaders(name);

while (values.hasMoreElements()) {

String value = values.nextElement();

headers.add(name, value);

}

}

InputStream inputStream = request.getInputStream();

RequestBody requestBody = null;

if (inputStream != null && HttpMethod.permitsRequestBody(method)) {

MediaType mediaType = null;

if (headers.get("Content-Type") != null) {

mediaType = MediaType.parse(headers.get("Content-Type"));

}

requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));

}

Request.Builder builder = new Request.Builder()

.headers(headers.build())

.url(uri)

.method(method, requestBody);

Response response = httpClient.newCall(builder.build()).execute();

LinkedMultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>();

for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {

responseHeaders.put(entry.getKey(), entry.getValue());

}

this.helper.setResponse(response.code(), response.body().byteStream(),

responseHeaders);

context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running

return null;

? ? }

}

上述過濾器將Servlet請求信息轉(zhuǎn)換為OkHttp3請求信息,執(zhí)行HTTP請求饶米,然后將OkHttp3響應(yīng)信息轉(zhuǎn)換為Servlet響應(yīng)桨啃。警告:此過濾器可能有錯誤车胡,但功能不正確。

如何編寫過濾器

后置過濾器通常操縱響應(yīng)照瘾。在下面的過濾器中匈棘,我們添加一個隨機(jī)UUID作為X-Foo頭。其他操作析命,如轉(zhuǎn)換響應(yīng)體主卫,要復(fù)雜得多,計(jì)算密集鹃愤。

public class AddResponseHeaderFilter extends ZuulFilter {

@Override

public String filterType() {

return POST_TYPE;

}

@Override

public int filterOrder() {

return SEND_RESPONSE_FILTER_ORDER - 1;

}

@Override

public boolean shouldFilter() {

return true;

}

@Override

public Object run() {

RequestContext context = RequestContext.getCurrentContext();

? ? HttpServletResponse servletResponse = context.getResponse();

servletResponse.addHeader("X-Foo", UUID.randomUUID().toString());

return null;

}

}

Zuul錯誤如何工作

如果在Zuul過濾器生命周期的任何部分拋出異常簇搅,則會執(zhí)行錯誤過濾器。SendErrorFilter只有RequestContext.getThrowable()不是null才會運(yùn)行软吐。然后在請求中設(shè)置特定的javax.servlet.error.*屬性瘩将,并將請求轉(zhuǎn)發(fā)到Spring Boot錯誤頁面。

Zuul渴望應(yīng)用程序上下文加載

Zuul內(nèi)部使用Ribbon調(diào)用遠(yuǎn)程URL凹耙,并且Ribbon客戶端默認(rèn)在第一次調(diào)用時由Spring Cloud加載依啰。可以使用以下配置更改Zuul的此行為霎终,并將導(dǎo)致在應(yīng)用程序啟動時谎势,子Ribbon相關(guān)的應(yīng)用程序上下文正在加載。

application.yml

zuul:

? ribbon:

? ? eager-load:

? ? ? enabled: true

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末虐沥,一起剝皮案震驚了整個濱河市熊经,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欲险,老刑警劉巖镐依,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異天试,居然都是意外死亡槐壳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門喜每,熙熙樓的掌柜王于貴愁眉苦臉地迎上來务唐,“玉大人,你說我怎么就攤上這事带兜》愕眩” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵刚照,是天一觀的道長刑巧。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么啊楚? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任吠冤,我火速辦了婚禮,結(jié)果婚禮上恭理,老公的妹妹穿的比我還像新娘拯辙。我一直安慰自己,他們只是感情好颜价,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布涯保。 她就那樣靜靜地躺著,像睡著了一般拍嵌。 火紅的嫁衣襯著肌膚如雪遭赂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天横辆,我揣著相機(jī)與錄音撇他,去河邊找鬼。 笑死狈蚤,一個胖子當(dāng)著我的面吹牛困肩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脆侮,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锌畸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了靖避?” 一聲冷哼從身側(cè)響起潭枣,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幻捏,沒想到半個月后盆犁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡篡九,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年谐岁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榛臼。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡伊佃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沛善,到底是詐尸還是另有隱情航揉,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布金刁,位于F島的核電站迷捧,受9級特大地震影響织咧,放射性物質(zhì)發(fā)生泄漏胀葱。R本人自食惡果不足惜漠秋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抵屿。 院中可真熱鬧庆锦,春花似錦、人聲如沸轧葛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尿扯。三九已至求晶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衷笋,已是汗流浹背芳杏。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辟宗,地道東北人爵赵。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像泊脐,于是被迫代替她去往敵國和親空幻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容