欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Ocelot简易教程(三)之主要特性及路由详解

程序员文章站 2022-05-25 14:38:55
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9664977.html 上篇《Ocelot简易教程(二)之快速开始2》教大家如何快速跑起来一个ocelot实例项目,也只是简单的对Ocelot进行了配置,这篇文章会给大家详细的介绍一下Ocelot的配置信息 ......

作者:依乐祝
原文地址:

上篇《ocelot简易教程(二)之快速开始2》教大家如何快速跑起来一个ocelot实例项目,也只是简单的对ocelot进行了配置,这篇文章会给大家详细的介绍一下ocelot的配置信息。希望能对大家深入使用ocelot有所帮助。

上篇中也提到了,最简单的ocelot如下面所示,只有简单的两个节点,一个是reroutes,另一个就是globalconfiguration关于这两个节点的作用,上篇也已经讲述了,这里再简单的讲下reroutes:告诉ocelot如何处理上游的请求。globalconfiguration:顾名思义就是全局配置,此节点的配置允许覆盖reroutes里面的配置,你可以在这里进行通用的一些配置信息。

{
    "reroutes": [],
    "globalconfiguration": {}
}

下面呢给出reroute 的所有的配置信息,当然在实际使用的时候你没有必要全部进行配置,只需要根据你项目的实际需要进行相关的配置就可以了。

"reroutes": [
    {
      "downstreampathtemplate": "/api/{everything}",//下游路由模板
      "upstreampathtemplate": "/good/{everything}",//上游路由模板
      "upstreamhttpmethod": [ "get", "post" ],//上游请求方法
      "addheaderstorequest": {},
      "upstreamheadertransform": {},
      "downstreamheadertransform": {},
      "addclaimstorequest": {},
      "routeclaimsrequirement": {},
      "addqueriestorequest": {},
      "requestidkey": null,
      "filecacheoptions": {
        "ttlseconds": 0,
        "region": null
      },
      "rerouteiscasesensitive": false,
      "servicename": null,
      "downstreamscheme": "http",
      "qosoptions": {//qos相关配置
        "exceptionsallowedbeforebreaking": 0,
        "durationofbreak": 0,
        "timeoutvalue": 0
      },
      "loadbalanceroptions": {//负载均衡相关选项
        "type": "roundrobin",
        "key": null,
        "expiry": 0
      },
      "ratelimitoptions": {//限流相关配置
        "clientwhitelist": [],
        "enableratelimiting": false,
        "period": null,
        "periodtimespan": 0.0,
        "limit": 0
      },
      "authenticationoptions": {//认证相关选项
        "authenticationproviderkey": null,
        "allowedscopes": []
      },
      "httphandleroptions": {//httphandler相关的配置
        "allowautoredirect": false,//是否对下游重定向进行响应
        "usecookiecontainer": false,//是否启动cookiecontainer储存cookies
        "usetracing": false,
        "useproxy": true
      },
      "downstreamhostandports": [//下游端口及host
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreamhost": null,//上游host
      "key": null,
      "delegatinghandlers": [],
      "priority": 1,
      "timeout": 0,
      "dangerousacceptanyservercertificatevalidator": false
    }

当然上面的配置项我就不一一的进行介绍,因为很多配置相信大家根据意思都能知道个大概了。我只会对比较常用的配置做下介绍。而且在接下来的文章中对对每个节点进行单独的详细的介绍。在介绍之前呢先看ocelot的几个特性。

ocelot特性介绍

合并配置文件

这个特性允许用户创建多个配置文件来方便的对大型项目进行配置。试想一下,如果你的项目有几十个路由规则需要配置的话,那么在一个配置文件进行配置应该很痛苦吧,有了这个特性后,你就可以创建多个配置文件。ocelot会自动合并他们。
在加载配置文件的时候 你可以通过下面的方式来调用addocelot()方法来替换直接加载某个配置的写法 如:addjsonfile(“ocelot.json”)

.configureappconfiguration((hostingcontext, config) =>
    {
        config
            .setbasepath(hostingcontext.hostingenvironment.contentrootpath)
            .addjsonfile("appsettings.json", true, true)
            .addjsonfile($"appsettings.{hostingcontext.hostingenvironment.environmentname}.json", true, true)
            .addocelot()
            .addenvironmentvariables();
    })

在这种情况下,ocelot会寻找所有匹配了 (?i)ocelot.([a-za-z0-9]*).json 的文件,然后合并他们。如何你要设置globalconfiguration 属性,那么你需要建立一个ocelot.global.json 的文件来进行全局的配置。

这里上一个例子吧!可以方便大家的理解。

新建一个ocelot.good.json文件,并加入下面的配置:

{
  "reroutes": [
    {
      "downstreampathtemplate": "/api/{everything}",
      "downstreamscheme": "http",
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreampathtemplate": "/good/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "loadbalanceroptions": {
        "type": "roundrobin"
      }
    }
  ]
}

然后再新建一个ocelot.order.json文件,并加入下面的配置:

{
  "reroutes": [
    {
      "downstreampathtemplate": "/api/{everything}",
      "downstreamscheme": "http",
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreampathtemplate": "/order/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "loadbalanceroptions": {
        "type": "roundrobin"
      }
    }
  ]
}

最后新建一个ocelot.all.json文件,并把上篇文章中的路由拷贝到里面:

{
  "reroutes": [
    {
      "downstreampathtemplate": "/api/{everything}",
      "downstreamscheme": "http",
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreampathtemplate": "/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "loadbalanceroptions": {
        "type": "roundrobin"
      }
    }
  ],
  "globalconfiguration": {

  }
}

然后修改下,program.cs文件中的代码如下:

public static iwebhostbuilder createwebhostbuilder(string[] args) =>
            webhost.createdefaultbuilder(args)
                .configureappconfiguration((hostingcontext, config) =>
                {
                    config
                        .setbasepath(hostingcontext.hostingenvironment.contentrootpath)
                        .addjsonfile("appsettings.json", true, true)
                        .addjsonfile($"appsettings.{hostingcontext.hostingenvironment.environmentname}.json", true, true)
                        .addocelot()
                        .addenvironmentvariables();
                })
                .useurls("http://localhost:1000")
                .usestartup<startup>();

这里最重要的代码就是config.addocelot()了。这段代码就会按照上面的规则查找所有符合条件的文件并合并路由。合并后的代码如下:

{
  "reroutes": [
    {
      "downstreampathtemplate": "/api/{everything}",
      "upstreampathtemplate": "/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "addheaderstorequest": {},
      "upstreamheadertransform": {},
      "downstreamheadertransform": {},
      "addclaimstorequest": {},
      "routeclaimsrequirement": {},
      "addqueriestorequest": {},
      "requestidkey": null,
      "filecacheoptions": {
        "ttlseconds": 0,
        "region": null
      },
      "rerouteiscasesensitive": false,
      "servicename": null,
      "downstreamscheme": "http",
      "qosoptions": {
        "exceptionsallowedbeforebreaking": 0,
        "durationofbreak": 0,
        "timeoutvalue": 0
      },
      "loadbalanceroptions": {
        "type": "roundrobin",
        "key": null,
        "expiry": 0
      },
      "ratelimitoptions": {
        "clientwhitelist": [],
        "enableratelimiting": false,
        "period": null,
        "periodtimespan": 0.0,
        "limit": 0
      },
      "authenticationoptions": {
        "authenticationproviderkey": null,
        "allowedscopes": []
      },
      "httphandleroptions": {
        "allowautoredirect": false,
        "usecookiecontainer": false,
        "usetracing": false,
        "useproxy": true
      },
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreamhost": null,
      "key": null,
      "delegatinghandlers": [],
      "priority": 1,
      "timeout": 0,
      "dangerousacceptanyservercertificatevalidator": false
    },
    {
      "downstreampathtemplate": "/api/{everything}",
      "upstreampathtemplate": "/good/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "addheaderstorequest": {},
      "upstreamheadertransform": {},
      "downstreamheadertransform": {},
      "addclaimstorequest": {},
      "routeclaimsrequirement": {},
      "addqueriestorequest": {},
      "requestidkey": null,
      "filecacheoptions": {
        "ttlseconds": 0,
        "region": null
      },
      "rerouteiscasesensitive": false,
      "servicename": null,
      "downstreamscheme": "http",
      "qosoptions": {
        "exceptionsallowedbeforebreaking": 0,
        "durationofbreak": 0,
        "timeoutvalue": 0
      },
      "loadbalanceroptions": {
        "type": "roundrobin",
        "key": null,
        "expiry": 0
      },
      "ratelimitoptions": {
        "clientwhitelist": [],
        "enableratelimiting": false,
        "period": null,
        "periodtimespan": 0.0,
        "limit": 0
      },
      "authenticationoptions": {
        "authenticationproviderkey": null,
        "allowedscopes": []
      },
      "httphandleroptions": {
        "allowautoredirect": false,
        "usecookiecontainer": false,
        "usetracing": false,
        "useproxy": true
      },
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreamhost": null,
      "key": null,
      "delegatinghandlers": [],
      "priority": 1,
      "timeout": 0,
      "dangerousacceptanyservercertificatevalidator": false
    },
    {
      "downstreampathtemplate": "/api/{everything}",
      "upstreampathtemplate": "/order/{everything}",
      "upstreamhttpmethod": [ "get", "post" ],
      "addheaderstorequest": {},
      "upstreamheadertransform": {},
      "downstreamheadertransform": {},
      "addclaimstorequest": {},
      "routeclaimsrequirement": {},
      "addqueriestorequest": {},
      "requestidkey": null,
      "filecacheoptions": {
        "ttlseconds": 0,
        "region": null
      },
      "rerouteiscasesensitive": false,
      "servicename": null,
      "downstreamscheme": "http",
      "qosoptions": {
        "exceptionsallowedbeforebreaking": 0,
        "durationofbreak": 0,
        "timeoutvalue": 0
      },
      "loadbalanceroptions": {
        "type": "roundrobin",
        "key": null,
        "expiry": 0
      },
      "ratelimitoptions": {
        "clientwhitelist": [],
        "enableratelimiting": false,
        "period": null,
        "periodtimespan": 0.0,
        "limit": 0
      },
      "authenticationoptions": {
        "authenticationproviderkey": null,
        "allowedscopes": []
      },
      "httphandleroptions": {
        "allowautoredirect": false,
        "usecookiecontainer": false,
        "usetracing": false,
        "useproxy": true
      },
      "downstreamhostandports": [
        {
          "host": "localhost",
          "port": 1001
        },
        {
          "host": "localhost",
          "port": 1002
        }
      ],
      "upstreamhost": null,
      "key": null,
      "delegatinghandlers": [],
      "priority": 1,
      "timeout": 0,
      "dangerousacceptanyservercertificatevalidator": false
    }
  ],
  "dynamicreroutes": [],
  "aggregates": [],
  "globalconfiguration": {
    "requestidkey": null,
    "servicediscoveryprovider": {
      "host": null,
      "port": 0,
      "type": null,
      "token": null,
      "configurationkey": null,
      "pollinginterval": 0
    },
    "ratelimitoptions": {
      "clientidheader": "clientid",
      "quotaexceededmessage": null,
      "ratelimitcounterprefix": "ocelot",
      "disableratelimitheaders": false,
      "httpstatuscode": 429
    },
    "qosoptions": {
      "exceptionsallowedbeforebreaking": 0,
      "durationofbreak": 0,
      "timeoutvalue": 0
    },
    "baseurl": null,
    "loadbalanceroptions": {
      "type": null,
      "key": null,
      "expiry": 0
    },
    "downstreamscheme": null,
    "httphandleroptions": {
      "allowautoredirect": false,
      "usecookiecontainer": false,
      "usetracing": false,
      "useproxy": true
    }
  }
}

ocelot的合并方式是先对满足格式的文件遍历查找,然后循环加载他们,并提取所有的reroutes以及aggregatereroutes 的数据。如果发现ocelot.global.json ,则添加到globalconfiguration 中。然后ocelto会将合并后的配置保存在ocelot.json的文件中,当ocelot运行时会加载这个合并后的ocelot.json文件,从而加载了所有的配置。

注意:这里需要注意的是ocelot在合并的过程中不会对内容进行验证,只有在最终合并的配置进行校验,所以如果发现问题的话,那么你需要检查最终生成的ocelot.json 是否出错了!

在consul中存储配置

这里你首先要做的就是安装ocelot中提供的consul的nuget包,nuget安装方式:

install-package ocelot.provider.consul

然后在注册服务时添加如下内容:ocelot将会尝试在consul kv存储并加载配置。

services
   .addocelot()
   .addconsul()
   .addconfigstoredinconsul();

当然你还得把下面的配置添加到你的ocelot.json文件中。这里定义ocelot如何查找consul根并从consul中加载并存储配置.

"globalconfiguration": {
    "servicediscoveryprovider": {
        "host": "localhost",
        "port": 9500
    }
}

变化时重新加载配置文件

ocelot支持在配置文件发生改变的时候重新加载json配置文件。在加载ocelot.json文件的时候按照下面进行配置,那么当你手动更新ocelot.json文件时,ocelot将重新加载ocelot.json配置文件。

config.addjsonfile("ocelot.json", optional: false, reloadonchange: true);

配置key

如果你使用consul进行配置,你可能需要配置key以便区分多个配置,为了指定key,你需要在json配置文件中的servicediscoveryprovider部分设置configurationkey属性:

"globalconfiguration": {
    "servicediscoveryprovider": {
        "host": "localhost",
        "port": 9500,
        "configurationkey": "oceolot_a"
    }
}

在此实例中,ocelot将会在consul查找时使用oceolot_a 作为配置的key.如果没有设置configurationkey 则ocelot将使用字符串internalconfiguration 作为此配置的key

跟踪重定向和使用cookiecontainer

在reroute配置中可以使用httphandleroptions来设置httphandler行为:

  1. allowautoredirect是一个值,指示请求是否应遵循重定向响应。如果请求应自动遵循来自下游资源的重定向响应,则将其设置为true; 否则是假的。默认值为false。
  2. usecookiecontainer是一个值,指示处理程序是否使用cookiecontainer属性存储服务器cookie并在发送请求时使用这些cookie。默认值为false。请注意,如果您使cookiecontainer,则ocelot会为每个下游服务缓存httpclient。这意味着对该downstreamservice的所有请求将共享相同的cookie。

ssl 错误处理

如果你想忽略ssl 警告/错误,你可以在你的reroute 配置中加上如下配置:

"dangerousacceptanyservercertificatevalidator": false

当然作者是不建议这样做的,最好的方式是创建你本地以及远程所信任的证书。

ocelot路由详解

路由

ocelot的最主要的功能是接收传入的http请求并将其转发到下游服务。

ocelot使用reroute节点描述将一个请求路由到另一个请求。为了让路由在ocelot中起作用,您需要在配置中设置reroute:

{
    "reroutes": [
    ]
}

要配置reroute,您需要在reroutes json数组中至少添加一个:

{
    "downstreampathtemplate": "/api/good/{goodid}",//下游路由模板
    "downstreamscheme": "http",//下游路由请求的方式
    "downstreamhostandports": [//下游路由的host以及端口
            {
                "host": "localhost",
                "port": 1001,
            }
        ],
    "upstreampathtemplate": "/good/{goodid}",//上游路由请求的模板
    "upstreamhttpmethod": [ "put", "delete" ]//上游路由请求的方式
}

downstreampathtemplate,downstreamscheme和downstreamhostandports定义请求将转发到的url。

downstreamhostandports是一个集合,用于定义您希望将请求转发到的任何下游服务的主机和端口。通常这只包含一个条目,但有时你希望对下游请求服务进行负载均衡,这个时候你就可以添加多个条目,并配合负载均衡选项进行相关的负载均衡设置。

upstreampathtemplate是ocelot用于标识要用于给定请求的downstreampathtemplate对应的url。使用upstreamhttpmethod以便ocelot可以区分具有不同http谓词的请求到相同的url。您可以设置特定的http方法列表,也可以设置一个空列表以允许所有的。

在ocelot中,您可以以{something}的形式将变量的占位符添加到模板中。占位符变量需要同时出现在downstreampathtemplate和upstreampathtemplate属性中。请求时ocelot将尝试请求时进行替换。

你也可以像下面这样配置,捕获所有的路由:

{
    "downstreampathtemplate": "/api/{everything}",
    "downstreamscheme": "http",
    "downstreamhostandports": [
            {
                "host": "localhost",
                "port": 1001,
            },
            {
                "host": "localhost",
                "port": 1002,
            }
        ],
    "upstreampathtemplate": "/{everything}",
    "upstreamhttpmethod": [ "get", "post" ]
}

这个配置将会把路径+查询字符串统统转发到下游路由.

注意:默认的rerouting的配置是不区分大小写的,如果需要修改此配置,可以通过下面进行配置:

"rerouteiscasesensitive": true

这意味着ocelot将尝试将传入的上游url与上游模板匹配时,区分大小写。

全部捕获

ocelot的路由还支持捕获所有样式路由,用户可以指定他们想要匹配所有请求。

如果您设置如下所示的配置,则所有请求都将直接代理。占位符{url}名称不重要,任何名称都可以使用。

{
    "downstreampathtemplate": "/{url}",
    "downstreamscheme": "http",
    "downstreamhostandports": [
            {
                "host": "localhost",
                "port": 1001,
            }
        ],
    "upstreampathtemplate": "/{url}",
    "upstreamhttpmethod": [ "get" ]
}

上面配置的全部捕获的优先级低于任何其他法人reroute。如果您的配置中还有下面的reroute,那么ocelot会在全部捕获之前匹配它。

{
    "downstreampathtemplate": "/",
    "downstreamscheme": "http",
    "downstreamhostandports": [
            {
                "host": "localhost",
                "port": 1001,
            }
        ],
    "upstreampathtemplate": "/",
    "upstreamhttpmethod": [ "get" ]
}

上游主机

此功能允许您根据上游主机获得reroutes。这通过查看客户端使用的主机头,然后将其用作我们用于识别reroute的信息的一部分来工作。

要使用此功能,请在配置中添加以下内容。

{
    "downstreampathtemplate": "/",
    "downstreamscheme": "http",
    "downstreamhostandports": [
            {
                "host": "localhost",
                "port": 1001,
            }
        ],
    "upstreampathtemplate": "/",
    "upstreamhttpmethod": [ "get" ],
    "upstreamhost": "yilezhu.cn"
}

仅当主机标头值为yilezhu.cn时,才会匹配上面的reroute。

如果您没有在reroute上设置upstreamhost,那么任何主机头都将与之匹配。这意味着如果你有两个相同的reroutes,除了upstreamhost,其中一个为null而另一个不为null 那么ocelot将支持已设置的那个。

优先级

你可以通过ocelot.json文件的reroutes节点中的priorty属性来设置匹配上游httprequest的优先级顺序
比如,下面两个路由:

{
    "upstreampathtemplate": "/goods/{catchall}"
    "priority": 0
}

以及

{
    "upstreampathtemplate": "/goods/delete"
    "priority": 1
}

上面两个路由中,如果向ocelot发出的请求时/goods/delete格式的话,则ocelot会优先匹配/goods /delete 的路由。

动态路由

作者的想法是在使用服务发现提供程序时启用动态路由,这样您就不必提供reroute的配置。我们会在服务发现那一章进行详细的介绍。

查询字符串

ocelot允许您指定一个查询字符串作为downstreampathtemplate的一部分,如下例所示。

{
    "reroutes": [
        {
            "downstreampathtemplate": "/api/subscriptions/{subscriptionid}/updates?unitid={unitid}",
            "upstreampathtemplate": "/api/units/{subscriptionid}/{unitid}/updates",
            "upstreamhttpmethod": [
                "get"
            ],
            "downstreamscheme": "http",
            "downstreamhostandports": [
                {
                    "host": "localhost",
                    "port": 50110
                }
            ]
        }
    ],
    "globalconfiguration": {
    }
}

在此示例中,ocelot将使用上游路径模板中{unitid}的值,并将其作为名为unitid的查询字符串参数添加到下游请求中!

ocelot还允许您将查询字符串参数放在upstreampathtemplate中,以便您可以将某些查询与某些服务匹配。

{
    "reroutes": [
        {
            "downstreampathtemplate": "/api/units/{subscriptionid}/{unitid}/updates",
            "upstreampathtemplate": "/api/subscriptions/{subscriptionid}/updates?unitid={unitid}",
            "upstreamhttpmethod": [
                "get"
            ],
            "downstreamscheme": "http",
            "downstreamhostandports": [
                {
                    "host": "localhost",
                    "port": 50110
                }
            ]
        }
    ],
    "globalconfiguration": {
    }
}

在此示例中,ocelot将仅匹配具有匹配的url路径的请求,并且查询字符串以unitid = something开头。您可以在此之后进行其他查询,但必须以匹配参数开头。此外,ocelot将交换查询字符串中的{unitid}参数,并在下游请求路径中使用它。

源码地址

当然是放上实例中的源码地址了:https://github.com/yilezhu/ocelotdemo

ocelot简易教程目录

  1. ocelot简易教程(一)之ocelot是什么
  2. ocelot简易教程(二)之快速开始1
  3. ocelot简易教程(二)之快速开始2
  4. ocelot简易教程(三)之主要特性及路由详解

    总结

    本文主要是对ocelot的新特性以及路由进行详细的介绍,这些介绍对你使用ocelot会有很大的帮助。下篇文章呢,我会对请求聚合以及服务发现以及动态路由进行记录,敬请期待!同时需要说明一点是,本文大部分内容是翻译自官方文档,当然中间穿插着自己在使用过程中一些理解,希望大家能够喜欢!