gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 32 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章浅谈Spring Cloud zuul http请求转发原理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

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

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

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

最后此篇关于浅谈Spring Cloud zuul http请求转发原理的文章就讲到这里了,如果你想了解更多关于浅谈Spring Cloud zuul http请求转发原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com