java http請求原理_淺談Spring Cloud zuul http請求轉發原理

spring cloud 網關,依賴于netflix 下的zuul 組件

zuul 的流程是,自定義 了ZuulServletFilter和zuulServlet兩種方式,讓開發者可以去實現,并調用

先來看下ZuulServletFilter的實現片段

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

try {

init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

try {

preRouting();

} catch (ZuulException e) {

error(e);

postRouting();

return;

}

// Only forward onto to the chain if a zuul response is not being sent

if (!RequestContext.getCurrentContext().sendZuulResponse()) {

filterChain.doFilter(servletRequest, servletResponse);

return;

}

try {

routing();

} catch (ZuulException e) {

error(e);

postRouting();

return;

}

try {

postRouting();

} catch (ZuulException e) {

error(e);

return;

}

} catch (Throwable e) {

error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));

} finally {

RequestContext.getCurrentContext().unset();

}

}

從上面的代碼可以看到,比較關心的是preRouting、routing,postRouting三個方法 ,這三個方法會調用 注冊為ZuulFilter的子類,首先來看下這三個方法

preRouting: 是路由前會做一些內容

routing():開始路由事項

postRouting:路由結束,不管是否有錯誤都會經過該方法

那這三個方法是怎么和ZuulFilter聯系在一起的呢?

先來分析下 preRouting:

void postRouting() throws ZuulException {

zuulRunner.postRoute();

}

同時 ZuulRunner再來調用

public void postRoute() throws ZuulException {

FilterProcessor.getInstance().postRoute();

}

最終調用 FilterProcessor的 runFilters

public void preRoute() throws ZuulException {

try {

runFilters("pre");

} catch (ZuulException e) {

throw e;

} catch (Throwable e) {

throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());

}

}

看到了runFilters 是通過 filterType(pre ,route ,post )來過濾出已經注冊的 ZuulFilter:

public Object runFilters(String sType) throws Throwable {

if (RequestContext.getCurrentContext().debugRouting()) {

Debug.addRoutingDebug("Invoking {" + sType + "} type filters");

}

boolean bResult = false;

//通過sType獲取 zuulFilter的列表

List list = FilterLoader.getInstance().getFiltersByType(sType);

if (list != null) {

for (int i = 0; i < list.size(); i++) {

ZuulFilter zuulFilter = list.get(i);

Object result = processZuulFilter(zuulFilter);

if (result != null && result instanceof Boolean) {

bResult |= ((Boolean) result);

}

}

}

return bResult;

}

再來看下 ZuulFilter的定義

public abstract class ZuulFilter implements IZuulFilter, Comparable {

private final DynamicBooleanProperty filterDisabled =

DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false);

/**

* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,

* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.

* We also support a "static" type for static responses see StaticResponseFilter.

* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)

*

* @return A String representing that type

*/

abstract public String filterType();

/**

* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not

* important for a filter. filterOrders do not need to be sequential.

*

* @return the int order of a filter

*/

abstract public int filterOrder();

/**

* By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false

*

* @return true by default

*/

public boolean isStaticFilter() {

return true;

}

只列出了一部分字段,但可以看到filterType和filterOrder兩個字段,這兩個分別是指定filter是什么類型,排序

這兩個決定了實現的ZuulFilter會在什么階段被執行,按什么順序執行

當選擇好已經注冊的ZuulFilter后,會調用ZuulFilter的runFilter

public ZuulFilterResult runFilter() {

ZuulFilterResult zr = new ZuulFilterResult();

if (!isFilterDisabled()) {

if (shouldFilter()) {

Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());

try {

Object res = run();

zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);

} catch (Throwable e) {

t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");

zr = new ZuulFilterResult(ExecutionStatus.FAILED);

zr.setException(e);

} finally {

t.stopAndLog();

}

} else {

zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);

}

}

return zr;

}

其中run 是一個ZuulFilter的一個抽象方法

public interface IZuulFilter {

/**

* a "true" return from this method means that the run() method should be invoked

*

* @return true if the run() method should be invoked. false will not invoke the run() method

*/

boolean shouldFilter();

/**

* if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter

*

* @return Some arbitrary artifact may be returned. Current implementation ignores it.

*/

Object run();

}

所以,實現ZuulFilter的子類要重寫 run方法,我們來看下 其中一個階段的實現 PreDecorationFilter 這個類是Spring Cloud封裝的在使用Zuul 作為轉發的代碼服務器時進行封裝的對象,目的是為了決定當前的要轉發的請求是按ServiceId,Http請求,還是forward來作轉發

@Override

public Object run() {

RequestContext ctx = RequestContext.getCurrentContext();

final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());

Route route = this.routeLocator.getMatchingRoute(requestURI);

if (route != null) {

String location = route.getLocation();

if (location != null) {

ctx.put("requestURI", route.getPath());

ctx.put("proxy", route.getId());

if (!route.isCustomSensitiveHeaders()) {

this.proxyRequestHelper

.addIgnoredHeaders(this.properties.getSensitiveHeaders().toArray(new String[0]));

}

else {

this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));

}

if (route.getRetryable() != null) {

ctx.put("retryable", route.getRetryable());

}

// 如果配置的轉發地址是http開頭,會設置 RouteHost

if (location.startsWith("http:") || location.startsWith("https:")) {

ctx.setRouteHost(getUrl(location));

ctx.addOriginResponseHeader("X-Zuul-Service", location);

}

// 如果配置的轉發地址forward,則會設置forward.to

else if (location.startsWith("forward:")) {

ctx.set("forward.to",

StringUtils.cleanPath(location.substring("forward:".length()) + route.getPath()));

ctx.setRouteHost(null);

return null;

}

else {

// 否則以serviceId進行轉發

// set serviceId for use in filters.route.RibbonRequest

ctx.set("serviceId", location);

ctx.setRouteHost(null);

ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);

}

if (this.properties.isAddProxyHeaders()) {

addProxyHeaders(ctx, route);

String xforwardedfor = ctx.getRequest().getHeader("X-Forwarded-For");

String remoteAddr = ctx.getRequest().getRemoteAddr();

if (xforwardedfor == null) {

xforwardedfor = remoteAddr;

}

else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates

xforwardedfor += ", " + remoteAddr;

}

ctx.addZuulRequestHeader("X-Forwarded-For", xforwardedfor);

}

if (this.properties.isAddHostHeader()) {

ctx.addZuulRequestHeader("Host", toHostHeader(ctx.getRequest()));

}

}

}

else {

log.warn("No route found for uri: " + requestURI);

String fallBackUri = requestURI;

String fallbackPrefix = this.dispatcherServletPath; // default fallback

// servlet is

// DispatcherServlet

if (RequestUtils.isZuulServletRequest()) {

// remove the Zuul servletPath from the requestUri

log.debug("zuulServletPath=" + this.properties.getServletPath());

fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");

log.debug("Replaced Zuul servlet path:" + fallBackUri);

}

else {

// remove the DispatcherServlet servletPath from the requestUri

log.debug("dispatcherServletPath=" + this.dispatcherServletPath);

fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");

log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);

}

if (!fallBackUri.startsWith("/")) {

fallBackUri = "/" + fallBackUri;

}

String forwardURI = fallbackPrefix + fallBackUri;

forwardURI = forwardURI.replaceAll("//", "/");

ctx.set("forward.to", forwardURI);

}

return null;

}

這個前置處理,是為了后面決定以哪種ZuulFilter來處理當前的請求 ,如 SimpleHostRoutingFilter,這個的filterType是post ,當 ``PreDecorationFilter設置了requestContext中的 RouteHost,如 SimpleHostRoutingFilter中的判斷

@Override

public boolean shouldFilter() {

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

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

}

在 SimpleHostRoutingFilter中的run中,真正實現地址轉發的內容,其實質是調用 httpClient進行請求

@Override

public Object run() {

RequestContext context = RequestContext.getCurrentContext();

HttpServletRequest request = context.getRequest();

MultiValueMap headers = this.helper

.buildZuulRequestHeaders(request);

MultiValueMap params = this.helper

.buildZuulRequestQueryParams(request);

String verb = getVerb(request);

InputStream requestEntity = getRequestBody(request);

if (request.getContentLength() < 0) {

context.setChunkedRequestBody();

}

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

this.helper.addIgnoredHeaders();

try {

HttpResponse response = forward(this.httpClient, verb, uri, request, headers,

params, requestEntity);

setResponse(response);

}

catch (Exception ex) {

context.set(ERROR_STATUS_CODE, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

context.set("error.exception", ex);

}

return null;

}

最后如果是成功能,會調用 注冊 為post的ZuulFilter ,目前有兩個 SendErrorFilter 和 SendResponseFilter 這兩個了,一個是處理錯誤,一個是處理成功的結果

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529530.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529530.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529530.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java堆外內存溢出_JVM 案例 - 堆外內存導致的溢出錯誤

案例一個網站為了實現客戶端實時從服務端接收數據&#xff0c;使用了 CometD 1.1.1 作為服務端推送框架&#xff0c;服務器是 Jetty7.1.4&#xff0c;CPU i5&#xff0c;內存 4G&#xff0c;操作系統 32位Windows。服務端常常拋出內存溢出異常&#xff0c;管理員把堆開到最大(3…

java mail outlook_已啟用Outlook API郵件與郵箱用戶

一個非常微妙的問題&#xff0c;也許是特定的環境 . 我正在嘗試使用Outlook 2010 API從啟用郵件的用戶中識別郵箱用戶 . 我們在Notes to Exchange遷移期間使用Dell Quest遷移工具&#xff0c;它是一個流動的項目 . 仍處于原型階段&#xff0c;因此使用VB宏來最終將在C&#xff…

oracle java存儲過程返回值_java程序調用Oracle 存儲過程 獲取返回值(無返回,非結果集,結果集)...

java程序調用Oracle 存儲過程 獲取返回值(無返回&#xff0c;非結果集&#xff0c;結果集)oracle中procedure是不能有返回值的&#xff0c;要想返回值&#xff0c;就得有輸出參數&#xff0c;同樣要想返回記錄集&#xff0c;可以把游標類型作為輸出參數。下面是詳細情況說明&am…

mysql dump工具升級_MySQL數據庫升級

當前不少系統的數據庫依舊是MySQL5.6&#xff0c;由于MySQL5.7及MySQL8.0在性能及安全方面有著很大的提升&#xff0c;因此需要升級數據庫。本文通過邏輯方式、物理方式原地升級來介紹MySQL5.6 升級至MySQL5.7的方法&#xff0c;并介紹其使用場景。1. 邏輯方式升級邏輯方式升級…

java int 128 ==_為什么 Java Integer 中“128==128”為false,而”100==100“為true?

這是一個挺有意思的討論話題&#xff0c;讓我們用代碼說話吧!運行下面的代碼:Integer a 128, b 128;System.out.println(a b);Integer c 100, d 100;System.out.println(c d);你會得到:falsetrue基本知識&#xff1a;我們知道&#xff0c;如果兩個引用指向同一個對象&…

mysql課程表學時_Mysql 鞏固提升 (學生表_課程表_成績表_教師表)

方便Mysql 鞏固提升創建表并插入數據&#xff1a;-- ------------------------------ Table structure for student-- ----------------------------DROP TABLE IF EXISTS student;CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT,sname varchar(32) DEFAULT NULL,s…

初始java_第一章__初始JAVA

1.java的三個發展方向&#xff1a;JAVASE(面向對象、API、JVM)、JAVAME(移動設備、游戲、通信)、JAVAEE(JSP、EJB、服務)2.開發JAVA的程序步驟&#xff1a;1.編寫源程序 2.編譯 3.運行3.JDKJRE開發工具下載java環境jdk 安裝并配置環境變量&#xff0c;.安裝直接下一步下一步直到…

python最常用的版本、也稱為classic_2021年中國大學《創新思維與創業》單元測試答案...

2021年中國大學《創新思維與創業》單元測試答案被人們稱為 “寒地水稻第一人”的是袁隆平答&#xff1a;錯地圖數據的基本特征包括答&#xff1a;時間屬性 空間定位屬性 地理屬性對賣方征稅導致商品價格上升答&#xff1a;√( )是在床榻上使用的一種矮形家具。答&#xff1a;炕…

java 泛型 繼承_java基礎之泛型的繼承

關于泛型的基本介紹和理解請參考以下幾篇文章&#xff0c;或查詢更多資料&#xff1a;本篇以簡單的List<>方式來進行說明。ArrayList繼承了List,ArrayList沒有繼承ListList>等價于List extends Object>請參考以下代碼&#xff1a;/*** author Ding Chengyun* 2014-…

appium java環境_Appium環境搭建(Windows版)

注&#xff1a;appium安裝到C盤&#xff0c;node.js安裝到C盤一、安裝node.js1、到官網下載node.js&#xff1a;https://nodejs.org/en/download/2、獲取到安裝文件后&#xff0c;直接雙擊安裝文件&#xff0c;根據程序的提示&#xff0c;完成nodejs的安裝。3、安裝完成后&…

ci mysql pdo_CI框架中pdo的使用方法

1、配置文件修改application/config文件夾下的database.php文件 $db[default] array(dsn > mysql:dbnameci_ecshop;host127.0.0.1,username > root,password > ,dbdriver > pdo,2、查詢操作$sql select * from aaa where id :id;$sql_array array(:id > …

ie11加載java插件_IE瀏覽器中ActiveX插件的使用

在某些行業的B/S應用系統中會不可避免的要用到ActiveX瀏覽器插件&#xff0c;而ActiveX插件只能在IE內核瀏覽器中運行&#xff0c;而常用的IE瀏覽器的版本眾多&#xff0c;從IE6到IE11&#xff0c;總共有6個版本&#xff0c;這就給開發的應用系統造成了不小的困擾&#xff1a;如…

netty java開發文檔_Netty簡明教學文檔

寫個簡單點&#xff0c;比較小白的文檔&#xff0c;言語比較接地氣Netty是什么&#xff1f;NIO的高層封裝&#xff0c;NIO很難寫&#xff0c;所以有了Netty&#xff0c;方便異步的操作service的主要代碼片段public void run() throws Exception {EventLoopGroup bossGroup new…

mysql 全局不重復_php uniqid() 通過MYSQL實現全局不重復的唯一ID

看了國外文章&#xff1a;https://jason.pureconcepts.net/2013/09/php-convert-uniqid-to-timestamp/ 不想寫&#xff50;&#xff48;&#xff50;腳本uniqid()處理&#xff0c;想到用mysql一次性把數據庫的ID改過來的方法&#xff0c;所以開始了以下研究方法一: 效率最高&…

java接口允許ajax訪問_服務允許AJAX請求,允許跨域請求

當工作時間&#xff0c;因為需要JS 進行AJAX請求&#xff0c;這時候存在跨域問題&#xff0c;當出現這種情況時&#xff0c;有多種方案解決比如使用JSONP&#xff0c;也有一種簡單的方式&#xff0c;就是在過濾器里面增加返回請求允許跨域head配置。代碼如下&#xff1a;/**** …

mysql的增_MySQL之增_insert-replace

MySQL增刪改查之增insert、replace一、INSERT語句帶有values子句的insert語句&#xff0c;用于數據的增加語法&#xff1a;INSERT [INTO] tbl_name[(col_name,...)]{VALUES | VALUE} (expr ,...),(...),...①用來把一個新行插入到表中②為和其它數據庫保持一致&#xff0c;不要…

python manager詳解_python 多進程共享全局變量之Manager()詳解

Manager支持的類型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。但當使用Manager處理list、dict等可變數據類型時&#xff0c;需要注意一個陷阱&#xff0c;即Manager對象無法監測到它引用的可變對象值的修改&#xff0c…

java 添加等待時間_Java中線程等待特定時間的最有效方法 - java

我知道這個問題here&#xff0c;但是我有一個稍微不同的問題。如果我希望自己通過各種Thread方法(而不是通過實用程序類或Quartz)手動編碼某個線程在特定時間的運行&#xff0c;那么最有效(就開銷而言)進行編碼。我考慮過:boolean wasInterrupted false;while (System.current…

PHP微信app接口退款,10.PHP接入微信退款接口

推薦文章摘要環境搭建開啟配置服務器環境核心類驗證回復拓展推薦文章今天網上和朋友圈炸開了鍋&#xff0c;原因是微信小程序正式上線了。吃瓜群眾表示不理解&#xff0c;于是去搜了下。不搜不要緊&#xff0c;搜了嚇一跳&#xff0c;原來微信小程序早在2016年9月份就已經進行了…

java線程讀取流的時候卡死,java – 線程中斷沒有結束阻塞調用輸入流讀取

我正在使用RXTX從串口讀取數據.讀取是在以下列方式生成的線程內完成的&#xff1a;CommPortIdentifier portIdentifier CommPortIdentifier.getPortIdentifier(port);CommPort comm portIdentifier.open("Whatever", 2000);SerialPort serial (SerialPort)comm;..…