浅谈Spring Cloud zuul http请求转发原理

2025-05-29 0 38

spring cloud 网关,依赖于netflix 下的zuul 组件

zuul 的流程是,自定义 了zuulservletfilter和zuulservlet两种方式,让开发者可以去实现,并调用

先来看下zuulservletfilter的实现片段

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37
@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:

?

1

2

3
void postrouting() throws zuulexception {

zuulrunner.postroute();

}

同时 zuulrunner再来调用

?

1

2

3
public void postroute() throws zuulexception {

filterprocessor.getinstance().postroute();

}

最终调用 filterprocessor runfilters

?

1

2

3

4

5

6

7

8

9
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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18
public object runfilters(string stype) throws throwable {

if (requestcontext.getcurrentcontext().debugrouting()) {

debug.addroutingdebug("invoking {" + stype + "} type filters");

}

boolean bresult = false;

//通过stype获取 zuulfilter的列表

list<zuulfilter> 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的定义

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31
public abstract class zuulfilter implements izuulfilter, comparable<zuulfilter> {

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21
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的一个抽象方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15
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来作转发

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86
@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中的判断

?

1

2

3

4

5
@override

public boolean shouldfilter() {

return requestcontext.getcurrentcontext().getroutehost() != null

&& requestcontext.getcurrentcontext().sendzuulresponse();

}

在 simplehostroutingfilter中的run中,真正实现地址转发的内容,其实质是调用 httpclient进行请求

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28
@override

public object run() {

requestcontext context = requestcontext.getcurrentcontext();

httpservletrequest request = context.getrequest();

multivaluemap<string, string> headers = this.helper

.buildzuulrequestheaders(request);

multivaluemap<string, string> 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 这两个了,一个是处理错误,一个是处理成功的结果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

原文链接:https://www.jianshu.com/p/295e51bc1518

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 浅谈Spring Cloud zuul http请求转发原理 https://www.kuaiidc.com/111144.html

相关文章

发表评论
暂无评论