This is the multi-page printable view of this section. Click here to print.
Use Cases
- 1: Route on GeoIP/ASN
- 2: Route on Subnet
- 3: Route on Content Type
- 4: Route on Content Popularity
- 5: Route on Selection Input
- 6: Adapt to multi-CDN
- 7: How to use ACD router for EDNS routing
- 8: How to use ESB3024 Router with Orbit Request Router
- 9: How to use ESB3024 Router with CoreDNS
- 10: How to use the Director as a replacement for Orbit Request Router
- 11: Route on Internally-Tracked Quality of Experience (QoE)
- 12: Use In-Stream Sessions
1 - Route on GeoIP/ASN
This page describes how to write configuration for GeoIP and ASN-based routing. For configuration in general, see Configuration.
To be able to do geographic based routing, the Director needs geographic location databases. See Geographic Databases for more information.
For more details on session groups and classifiers, see Session Groups and Classification.
ASN
Routing on ASN is done through a combination of session group classifiers and Lua script weight functions. We need to create ASN classifier(s) and then associate them with a session group:
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: geoip
Adding a 'geoip' element
classifier : {
name (default: ): allowed_asn_classifier
type (default: geoip): ⏎
inverted (default: False): ⏎
continent (default: ): ⏎
country (default: ): ⏎
cities : [
city (default: ): ⏎
Add another 'city' element to array 'cities'? [y/N]: ⏎
]
asn (default: ): Agile*ISP
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "allowed_asn_classifier",
"type": "geoip",
"inverted": false,
"continent": "",
"country": "",
"cities": [],
"asn": "Agile*ISP"
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): allowed_asn
classifiers : [
classifier (default: ): allowed_asn_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "allowed_asn",
"classifiers": [
"allowed_asn_classifier"
]
}
]
}
Merge and apply the config? [y/n]: y{
"session_groups": [
{
"id": 1,
"name": "allowed_asn",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_asn_classifier",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"asn": "Agile*ISP"
}
}
]
]
}
]
}The asn value is a string, matching an ISP name or similar. It supports
wildcard matching using asterisks and is case insensitive.
Note that each ASN classifier has to be in its own list. This is because classifiers within the same inner list all have to match for the entire list to be true, and that is impossible for classifiers that match against different ASNs. When the classifiers are in their own lists, it’s enough that one of them matches for the outer classifier list to also match.
Simple Example
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: split
Adding a 'split' element
rule : {
name (default: ): asn_split_node
type (default: split): ⏎
rule (default: ): return in_session_group("allowed_asn")
onMatch (default: ): allowed-asn-host
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "asn_split_node",
"type": "split",
"condition": "in_session_group('allowed_asn')",
"onMatch": "allowed-asn-host",
"onMiss": "offload-host"
}
]
}
Merge and apply the config? [y/n]: y{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"session_groups": [
{
"id": 1,
"name": "allowed_asn",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_asn_classifier_agile",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"asn": "Agile*ISP"
}
}
],
[
{
"id": 1,
"inverted": false,
"name": "allowed_asn_classifier_edgeware",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"asn": "Edgeware *"
}
}
]
]
}
],
"cdns": [
{
"id": "basic-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "default-host",
"cdn_id": "basic-cdn",
"address_family": "ipv4",
"host": "cdn-host.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"address_family": "ipv4",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "default-node",
"host-id": "default-host",
"weight_function": "return in_session_group('allowed_asn')"
},
{
"id": "offload-node",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
}
}Geographical Location
Routing on geographical location is done in a similar fashion. Once again, we create location classifier(s) and associate them with a session group:
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: geoip
Adding a 'geoip' element
classifier : {
name (default: ): allowed_location
type (default: geoip): ⏎
inverted (default: False): ⏎
continent (default: ): ⏎
country (default: ): ⏎
cities : [
city (default: ): stockholm
Add another 'city' element to array 'cities'? [y/N]: ⏎
]
asn (default: ): ⏎
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "allowed_location",
"type": "geoip",
"inverted": false,
"continent": "",
"country": "",
"cities": [
"stockholm"
],
"asn": ""
}
]
}
Merge and apply the config? [y/n]: y "session_groups": [
{
"id": 1,
"name": "allowed_location",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_location_classifier",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"continent": "",
"country": "",
"region": "",
"cities": ["stockholm"]
}
}
]
]
}
]At least one of the optional fields continent, country, region and
cities are required. The classifier matches a request when all specified
fields do (for the cities list, one matching member is sufficient). All
values support wildcard matching using asterisks and are case insensitive.
Note that each location classifier has to be in its own list. This is because classifiers within the same inner list all have to match for the entire list to be true, and that is impossible for classifiers that match against different locations. When the classifiers are in their own lists, it’s enough that one of them matches for the outer classifier list to also match.
Simple Example
confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: split
Adding a 'split' element
rule : {
name (default: ): geographic_location_split_node
type (default: split): ⏎
rule (default: ): return in_session_group("allowed_location")
onMatch (default: ): allowed-location-host
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "geographic_location_split_node",
"type": "split",
"condition": "in_session_group('allowed_location')",
"onMatch": "allowed-location-host",
"onMiss": "offload-host"
}
]
}
Merge and apply the config? [y/n]: y{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"session_groups": [
{
"id": 1,
"name": "allowed_location",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_location_classifier_sweden",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"country": "Sweden"
}
}
],
[
{
"id": 1,
"inverted": false,
"name": "allowed_location_classifier_oslo",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"country": "Norway",
"cities": ["Oslo"]
}
}
]
]
}
],
"cdns": [
{
"id": "basic-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "default-host",
"cdn_id": "basic-cdn",
"address_family": "ipv4",
"host": "cdn-host.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"address_family": "ipv4",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "default-node",
"host_id": "default-host",
"weight_function": "return in_session_group('allowed_location')"
}
{
"id": "offload-node",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
}
}2 - Route on Subnet
This page describes how to write configuration for subnet-based routing. For configuration in general, see Configuration.
For details on the subnets API, see Subnets API.
Subnet-based routing can be done either by using session groups and classifiers or by using the subnets API, allowing large amounts of named subnets.
Session Group Classifiers
Routing on subnets can be done through a combination of session group classifiers and Lua script weight functions. Configuring session groups and classifiers based on subnets and using them in routing may look like:
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: ipranges
Adding a 'ipranges' element
classifier : {
name (default: ): ip_ranges_classifier
type (default: ipranges): ⏎
inverted (default: False): ⏎
ipranges : [
iprange (default: ): 10.0.0.0/8
Add another 'iprange' element to array 'ipranges'? [y/N]: y
iprange (default: ): 192.168.0.0/16
Add another 'iprange' element to array 'ipranges'? [y/N]: y
iprange (default: ): 2001:0db8:85a3::/128
Add another 'iprange' element to array 'ipranges'? [y/N]: ⏎
]
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "ip_ranges_classifier",
"type": "ipranges",
"inverted": false,
"ipranges": [
"10.0.0.0/8",
"192.168.0.0/16",
"2001:0db8:85a3::/128"
]
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): allowed_subnets
classifiers : [
classifier (default: ): ip_ranges_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "allowed_subnets",
"classifiers": [
"ip_ranges_classifier"
]
}
]
}
Merge and apply the config? [y/n]: y{
"session_groups": [
{
"id": 1,
"name": "allowed_subnets",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_subnet_classifier",
"rule": {
"rule_type": "ip_ranges_rule",
"source": "session/client_ip",
"ip_ranges": ["10.0.0.0/8", "192.168.0.0/16", "2001:0db8:85a3::/128"]
}
}
]
]
}
]
}The field ip_ranges is a list of CIDR-notation strings, supporting both IPv4
and IPv6 format. Incoming requests have their IP tested against the listed
ranges. The classifier evaluates as true if any of the ranges in the
classifier’s list matches the IP address of the request.
These session groups can then be used in routing as follows:
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: split
Adding a 'split' element
rule : {
name (default: ): subnet_split_node
type (default: split): ⏎
rule (default: ): return in_session_group('allowed_subnets')
onMatch (default: ): allowed-host
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "subnet_split_node",
"type": "split",
"condition": "in_session_group('allowed_subnets')",
"onMatch": "allowed-host",
"onMiss": "offload-host"
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.entrypoint subnet_split_node
services.routing.entrypoint = 'subnet_split_node'{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"cdns": [
{
"id": "allowed-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "allowed-host",
"cdn_id": "allowed-cdn",
"host": "allowed-host.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "allowed-node",
"host_id": "allowed-host",
"weight_function": "return in_session_group('allowed_subnets')"
},
{
"id": "offload-node",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
},
"session_groups": [
{
"id": 1,
"name": "allowed_subnets",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "allowed_subnet_classifier",
"rule": {
"rule_type": "ip_ranges_rule",
"source": "session/client_ip",
"ip_ranges": ["10.0.0.0/8", "192.168.0.0/16", "2001:0db8:85a3::/128"]
}
}
]
]
}
]
}Named Subnets
Named subnets are injected into the router in the form of JSON payloads to the Subnets API. Once the subnet data has been fed to the router, Lua functions can access the subnet name associated with an incoming request and use the result when it performs routing.
Note that confcli cannot be used to inject named subnets into the router.
Assume we have injected the following subnet configuration into the router:
{
"10.0.0.0/8": "test_net_4",
"192.168.0.0/16": "test_net_4",
"2001:0db8:85a3::/128": "test_net_6"
}
A router configuration using this subnet configuration can then be constructed as:
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: split
Adding a 'split' element
rule : {
name (default: ): subnet_split_node
type (default: split): ⏎
rule (default: ): in_subnet('test_net_4')
onMatch (default: ): allowed-host-4
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "subnet_split_node",
"type": "split",
"condition": "in_subnet('test_net_4')",
"onMatch": "allowed-host-4",
"onMiss": "offload-host"
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.entrypoint subnet_split_node
services.routing.entrypoint = 'subnet_split_node'{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"cdns": [
{
"id": "allowed-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "allowed-host-4",
"cdn_id": "allowed-cdn",
"address_family": "ipv4",
"host": "allowed-host4.example"
},
{
"id": "allowed-host-6",
"cdn_id": "allowed-cdn",
"address_family": "ipv6",
"host": "allowed-host6.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"address_family": "ipv4",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "allowed-node-4",
"host_id": "allowed-host-4",
"weight_function": "return in_subnet('test_net_4')"
},
{
"id": "allowed-node-6",
"host_id": "allowed-host-6",
"weight_function": "return in_subnet('test_net_6')"
},
{
"id": "offload-node",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
}
}Note the absence of session groups in this configuration, instead relying solely on the named subnet API to making routing decisions based on subnets.
3 - Route on Content Type
This page describes how to write configuration for content-based routing. For configuration in general, see Configuration.
Two ways to route on content type will be demonstrated: content path based
and hostname based using session groups and classifiers. For more details on
session groups and classifiers, see
Session Groups and Classification.
Content Path
Routing on content path is done through a combination of session group
classifiers and Lua script weight functions. Two suitable classifiers for
content path matching are string_match_rule and regex_rule.
The pattern is a string matching the path segment of a request URL, including
the file name. When selecting the string_match_rule type, use asterisks for
wildcard matching. When selecting the regex_rule follow the C++11 std::regex
ECMAScript
syntax to make valid patterns. Make sure to write patterns that match the
entire path segment, not just part of it.
Note that each content classifier normally has to be in its own list. This is because classifiers within the same inner list all have to match for the entire list to be true, and that is impossible for classifiers that match against different content paths. When the classifiers are in their own lists, it’s enough that one of them matches for the outer classifier list to also match.
Simple Example
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: contentUrlPath
Adding a 'contentUrlPath' element
classifier : {
name (default: ): live_content_classifier
type (default: contentUrlPath): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): ⏎
pattern (default: ): *live_tv*
}
Add another 'classifier' element to array 'classifiers'? [y/N]: y
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: contentUrlPath
Adding a 'contentUrlPath' element
classifier : {
name (default: ): news_content_classifier
type (default: contentUrlPath): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): regex
pattern (default: ): /([^/]+/)?news_reports_\\d+/.*
}
Add another 'classifier' element to array 'classifiers'? [y/N]: y
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: contentUrlPath
Adding a 'contentUrlPath' element
classifier : {
name (default: ): hls_content_classifier
type (default: contentUrlPath): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): ⏎
pattern (default: ): *.m3u8
}
Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
"classifiers": [
{
"name": "live_content_classifier",
"type": "contentUrlPath",
"inverted": false,
"patternType": "stringMatch",
"pattern": "*live_tv*"
},
{
"name": "news_content_classifier",
"type": "contentUrlPath",
"inverted": false,
"patternType": "regex",
"pattern": "/([^/]+/)?news_reports_\\\\d+/.*"
},
{
"name": "hls_content_classifier",
"type": "contentUrlPath",
"inverted": false,
"patternType": "stringMatch",
"pattern": "*.m3u8"
}
]
}
Merge and apply the config? [y/n]: y
confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): live_content
classifiers : [
classifier (default: ): live_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: y
sessionGroup : {
name (default: ): news_content
classifiers : [
classifier (default: ): news_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: y
sessionGroup : {
name (default: ): hls_content
classifiers : [
classifier (default: ): hls_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "live_content",
"classifiers": [
"live_content_classifier"
]
},
{
"name": "news_content",
"classifiers": [
"news_content_classifier"
]
},
{
"name": "hls_content",
"classifiers": [
"hls_content_classifier"
]
}
]
}
Merge and apply the config? [y/n]: y{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"session_groups": [
{
"id": 1,
"name": "live_content",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "live_content_classifier",
"rule": {
"rule_type": "string_match_rule",
"source": "session/content_url_path",
"pattern": "*live_tv*"
}
}
]
],
[
[
{
"id": 2,
"inverted": false,
"name": "news_content_classifier",
"rule": {
"rule_type": "regex_rule",
"source": "session/content_url_path",
"pattern": "/([^/]+/)?news_reports_\\d+/.*"
}
}
]
]
},
{
"id": 2,
"name": "hls_content",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "hls_content_classifier",
"rule": {
"rule_type": "string_match_rule",
"source": "session/content_url_path",
"pattern": "*.m3u8"
}
}
]
]
}
],
"cdns": [
{
"id": "live-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "vod-hls-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "vod-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "live-host",
"cdn_id": "live-cdn",
"address_family": "ipv4",
"host": "live-host.example"
},
{
"id": "vod-hls-host",
"cdn_id": "vod-hls-cdn",
"address_family": "ipv4",
"host": "vod-hls-host.example"
},
{
"id": "vod-host",
"cdn_id": "vod-cdn",
"address_family": "ipv4",
"host": "vod-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "live-node",
"host_id": "live-host",
"weight_function": "return session_groups.live_content and 1 or 0"
},
{
"id": "vod-hls-node",
"host_id": "vod-hls-host",
"weight_function": "return session_groups.hls_content and 1 or 0"
},
{
"id": "vod-node",
"host_id": "vod-host",
"weight_function": "return 1"
}
]
}
}Hostname
In cases where there are separate domain or subdomain names for different content types, it’s simple to make classifiers that filter on those names.
The pattern is a string matching the hostname segment of a request URL,
including the file name. When selecting the string_match_rule type, use
asterisks for wildcard matching. When selecting the regex_rule follow the
C++11 std::regex
ECMAScript
syntax to make valid patterns. Make sure to write patterns that match the
entire hostname segment, not just part of it.
Note that each hostname classifier has to be in its own list. This is because classifiers within the same inner list all have to match for the entire list to be true, and that is impossible for classifiers that match against different hostnames. When the classifiers are in their own lists, it’s enough that one of them matches for the outer classifier list to also match.
Simple Example
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: hostName
Adding a 'hostName' element
classifier : {
name (default: ): live_content_classifier
type (default: hostName): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): ⏎
pattern (default: ): live.example.com
}
Add another 'classifier' element to array 'classifiers'? [y/N]: y
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: hostName
Adding a 'hostName' element
classifier : {
name (default: ): news_content_classifier
type (default: hostName): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): regex
pattern (default: ): /([^\\.]+/)\\.news_reports_\\d+/\\.example\\.com
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "live_content_classifier",
"type": "hostName",
"inverted": false,
"patternType": "stringMatch",
"pattern": "live.example.com"
},
{
"name": "news_content_classifier",
"type": "hostName",
"inverted": false,
"patternType": "regex",
"pattern": "/([^\\\\.]+/)\\\\.news_reports_\\\\d+/\\\\.example\\\\.com"
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): live_content
classifiers : [
classifier (default: ): live_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: y
sessionGroup : {
name (default: ): news_content
classifiers : [
classifier (default: ): news_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: y
sessionGroup : {
name (default: ): hls_content
classifiers : [
classifier (default: ): hls_content_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "live_content",
"classifiers": [
"live_content_classifier"
]
},
{
"name": "news_content",
"classifiers": [
"news_content_classifier"
]
},
{
"name": "hls_content",
"classifiers": [
"hls_content_classifier"
]
}
]
}{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"session_groups": [
{
"id": 1,
"name": "live_content",
"classifiers": [
[
{
"id": 1,
"inverted": false,
"name": "live_content_classifier",
"rule": {
"rule_type": "string_match_rule",
"source": "session/hostname",
"pattern": "live.example.com"
}
}
],
[
{
"id": 2,
"inverted": false,
"name": "news_content_classifier",
"rule": {
"rule_type": "regex_rule",
"source": "session/hostname",
"pattern": "/([^\\.]+/)\\.news_reports_\\d+/\\.example\\.com"
}
}
]
]
}
],
"cdns": [
{
"id": "live-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "vod-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "live-host",
"cdn_id": "live-cdn",
"address_family": "ipv4",
"host": "live-host.example"
},
{
"id": "vod-host",
"cdn_id": "vod-cdn",
"address_family": "ipv4",
"host": "vod-host.example"
}
],
"routing": {
"id": "routing_table",
"member_order": "sequential",
"members": [
{
"id": "live-node",
"host_id": "live-host",
"weight_function": "return session_groups.live_content and 1 or 0"
},
{
"id": "vod-node",
"host_id": "vod-host",
"weight_function": "return 1"
}
]
}
}Using the Session Groups in Confcli
The constructed session groups can be used in routing by creating a rule that references them:
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: firstMatch
Adding a 'firstMatch' element
rule : {
name (default: ): classifier_routing_rule
type (default: firstMatch): ⏎
targets : [
target : {
onMatch (default: ): live-host
rule (default: ): in_all_session_groups('live_content', 'news_content')
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
onMatch (default: ): vod-hls-host
rule (default: ): in_session_group('hls_content')
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
onMatch (default: ): offload-host
rule (default: ): always()
}
Add another 'target' element to array 'targets'? [y/N]: ⏎
]
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "classifier_routing_rule",
"type": "firstMatch",
"targets": [
{
"onMatch": "live-host",
"condition": "in_all_session_groups('live_content', 'news_content')"
},
{
"onMatch": "vod-hls-host",
"condition": "in_session_group('hls_content')"
},
{
"onMatch": "offload-host",
"condition": "always()"
}
],
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.entrypoint classifier_routing_rule
services.routing.entrypoint = classifier_routing_rule
4 - Route on Content Popularity
This page describes how to write configuration for content popularity routing. For configuration in general, see Configuration.
For more details on content popularity tuning and routing, see
Content Popularity
The router tracks content popularity which can be utilized for routing.
Using the configuration tool confcli, creating a content popularity based
routing configuration for hierarchical and multi-edge scenarios will be
demonstrated.
Hierarchical
Consider a CDN setup with edge streamers that has cached popular content and a central streamer where all content is available. You can decide where to route clients based on the requested content’s popularity.
Assuming that appropriate streamer hosts have already been configured, configuration will look like:
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: contentPopularity
Adding a 'contentPopularity' element
rule : {
name (default: ): contentPopularityHierarchy
type (default: contentPopularity): ⏎
popularityThreshold (default: 10): 2000
onPopular (default: ): edgeStreamer
onUnpopular (default: ): centralStreamer
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "contentPopularityHierarchy",
"type": "contentPopularity",
"popularityThreshold": 2000.0,
"onPopular": "edgeStreamer",
"onUnpopular": "offloadStreamer"
}
]
}
Merge and apply the config? [y/n]: y
where name is the name of the rule, isPopular is the rule to route to if
the content is popular, otherwise the rule isUnpopular is routed to. Lastly,
popularityThreshold is the threshold for which content is considered popular.
Configuring popularityThreshold = 2001 means that the top 2000 most popular
assets will be routed to edgeStreamer.
The rule contentPopularityHierarchy can then be used to construct your routing
tree.
Multi-Edge
Consider a CDN setup with three edge streamers, edge1, edge2 and
edge3, configured to cache very popular content, mildly popular content
and unpopular content respectively. Assuming that appropriate streamer
hosts have already been configured, the following configuration can be used for
correctly routing a request in this scenario:
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: contentPopularity
Adding a 'contentPopularity' element
rule : {
name (default: ): mildlyAndUnpopular
type (default: contentPopularity): ⏎
popularityThreshold (default: 10): 2001
onPopular (default: ): edge2
onUnpopular (default: ): edge3
}
Add another 'rule' element to array 'rules'? [y/N]: y
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: contentPopularity
Adding a 'contentPopularity' element
rule : {
name (default: ): multiLevelPopularity
type (default: contentPopularity): ⏎
popularityThreshold (default: 10): 101
onPopular (default: ): edge1
onUnpopular (default: ): mildyAndUnpopular
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "mildlyAndUnpopular",
"type": "contentPopularity",
"popularityThreshold": 2001.0,
"onPopular": "edge2",
"onUnpopular": "edge3"
},
{
"name": "multiLevelPopularity",
"type": "contentPopularity",
"popularityThreshold": 101.0,
"onPopular": "edge1",
"onUnpopular": "mildyAndUnpopular"
}
]
}
Merge and apply the config? [y/n]: y
By configuring a rule to route to multiLevelPopularity,
this configuration will route requests for the top 100 most popular content
to edge1, popularity ranking 101-2000 to edge2 and popularity
ranking > 2000 to edge3. Since the contentPopularity rule offers a binary
routing choice of either isPopular or isUnpopular, multiple
contentPopularity rules can be utilized to construct multi-leveled content
popularity based routing configurations.
Application
To use any of these two configurations in your installation, you’ll need to
configure either a rule or the entrypoint to route to either
multiLevelPopularity or contentPopularityHierarchy.
5 - Route on Selection Input
This page describes how to write configuration for routing on bandwidth usage or number of ongoing sessions in AgileTV CDN:s by using the selection input API. For configuration in general, see Configuration.
For more details on the selection input API, see Selection Input.
Selection Input
The selection input API allows you to inject custom data, that can be used during routing, into the router. For this use case, we will use CDN bandwidth and the number of ongoing sessions to demonstrate the capabilities of the selection input API.
Imagine that we have a monitor constantly polling the bandwidth and ongoing sessions of hosts in a CDN. These values are injected into the selection input API as JSON packets:
{
"edge-host-available-bps": 1000000000,
"edge-host-ongoing-sessions": 300
}
These keys will end up in the router Lua context as “selection inputs”, arbitrary values or JSON objects that can be used to calculate routing weights.
The selection input variable edge-host-available-bps will be available
through either selection_input.edge-host-available-bps or
si('edge-host-available-bps'). The function si(si_var)
fetches the selection input value if it exists, otherwise it returns 0.
However, when comparing selection input variables with numbers, there are a
number of built-in Lua functions that will simplify things, such as gt() and
lt(). For a complete list and more details on these functions, see
Built-in Functions.
Simple Example
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: firstMatch
Adding a 'firstMatch' element
rule : {
name (default: ): selection-input-routing
type (default: firstMatch): ⏎
targets : [
target : {
onMatch (default: ): edge-host
rule (default: ): lt('edge_host_ongoing_sessions', 1000)
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
onMatch (default: ): edge-host
rule (default: ): gt('edge_host_available_bps', 5000000)
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
onMatch (default: ): offload-host
rule (default: ): always()
}
Add another 'target' element to array 'targets'? [y/N]: ⏎
]
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "selection-input-routing",
"type": "firstMatch",
"targets": [
{
"onMatch": "edge-host",
"condition": "lt('edge_host_ongoing_sessions', 1000)"
},
{
"onMatch": "edge-host",
"condition": "gt('edge_host_available_bps', 5000000)"
},
{
"onMatch": "offload-host",
"condition": "always()"
}
]
}
]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.entrypoint selection-input-routing
services.routing.entrypoint = 'selection-input-routing'{
"content_server": {
"http_enable": true,
"http_port": 80,
"https_enable": true,
"https_port": 443
},
"cdns": [
{
"id": "edge-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "edge-host",
"cdn_id": "edge-cdn",
"host": "edge-host.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing-table",
"member_order": "sequential",
"members": [
{
"id": "edge-host-sessions-available",
"host_id": "edge-host",
"weight_function": "return (lt('edge_host_ongoing_sessions', 1000) ~= 0) and 1 or 0"
},
{
"id": "edge-host-bandwith-available",
"host_id": "edge-host",
"weight_function": "return (gt('edge_host_available_bps', 5000000) ~= 0) and 1 or 0"
},
{
"id": "offload",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
}
}6 - Adapt to multi-CDN
This page describes how to write configuration for setups that require the URL sent back to the client to be rewritten based on routing results, for example by adding a token to the path or as a query string parameter. For configuration in general, see Configuration.
In order to change the redirect location URL, we need to add a response translation function to the confd configuration. By default, all translation functions are empty:
$ confcli services.routing.translationFunctions
{
"translationFunctions": {
"request": "",
"response": ""
}
}
For full documentation of the response translation function, please see Lua Hooks.
Simple Example
Translation functions tend to become large, which makes it hard to write
them inside a single string. Instead, the function can be written in a separate
.lua script file that is uploaded to the router and referenced from the
confd configuration.
The following example will take this approach, adding a script called
"rewrite.lua" to the configured custom Lua folder /tmp/custom_lua on
the router. For more information see the Configuration JSON Overview.
Lua Script
The following script assumes the existence of the function parse_url that
returns an object with URL segments that can be altered and then combined into
a URL string using the tostring(obj) function again.
Beware that the router does not validate the URL in the Location header, it is up to the Lua script writer to ensure that only URLs are returned.
function response_rewrite(Headers)
-- The live server has the token as part of the path, added first before the
-- path from the request.
local location = parse_url(response_headers.location)
if location.host == "live-host.example" then
location.path = "/live-prefix" .. location.path
return HTTPResponse(
{
Headers = {
{"location", tostring(location)}
}
}
)
end
-- The VOD server expects the token as a query string rather than as part
-- of the path segment.
-- Since VOD can be used for offload, check that session_groups.is_vod
-- is set as well.
if location.host == "vod-host.example" and session_groups.is_vod then
location.query["token"] = md5sum(secret_key .. request.path)
return HTTPResponse(
{
Headers = {
{"location", tostring(location)}
}
}
)
end
-- If the request was neither in the is_live session group, nor contained
-- the path element "vod", we return a nil object to indicate that we don't
-- want to change anything in the response.
return nil
end
Configuration
To configure a response translation function using with the custom Lua function
response_rewrite() using confcli, run the command:
$ confcli services.routing.translationFunctions.response 'return response_rewrite()'
services.routing.translationFunctions.response = 'return response_rewrite()'
An example confd configuration utilizing this translation function based on
session groups would look like this:
$ confcli services.routing
{
"translationFunctions": {
"request": "",
"response": "return response_rewrite()"
},
"sessionGroups": [
{
"name": "is_live",
"classifiers": [
"is_live_classifier"
]
},
{
"name": "is_vod",
"classifiers": [
"is_vod_classifier"
]
}
],
"classifiers": [
{
"name": "is_live_classifier",
"type": "contentUrlPath",
"inverted": false,
"patternType": "stringMatch",
"pattern": "*/live/*"
},
{
"name": "is_vod_classifier",
"type": "contentUrlPath",
"inverted": false,
"patternType": "stringMatch",
"pattern": "*/vod/*"
}
],
"hostGroups": [
{
"name": "live-cdn",
"type": "host",
"httpPort": 80,
"httpsPort": 443,
"hosts": [
{
"name": "live-host",
"hostname": "live-host.example",
"ipv6_address": ""
}
]
},
{
"name": "vod-cdn",
"type": "host",
"httpPort": 80,
"httpsPort": 443,
"hosts": [
{
"name": "vod-host",
"hostname": "vod-host.example",
"ipv6_address": ""
}
]
}
],
"rules": [
{
"name": "multiCdnExample",
"type": "firstMatch",
"targets": [
{
"condition": "in_session_group('is_live')",
"onMatch": "live-host"
},
{
"condition": "in_session_group('is_vod')",
"onMatch": "vod-host"
},
// Use the vod-host as offload for any content, but don't set any
// token for requests not classified as vod.
{
"condition": "always()",
"onMatch": "vod-host"
}
]
}
],
"entrypoint": "multiCdnExample",
"applyConfig": true
}
7 - How to use ACD router for EDNS routing
This page describes how to write configuration for EDNS routing. For configuration in general, see Configuration.
EDNS Routing
In your CDN architecture, you might utilize EDNS routing to redirect clients. Whether it’s your own CDN or a third party CDN, the ESB3024 Router supports creating EDNS requests and sending them to any DNS server.
Configuration
To configure EDNS routing, you first need to create a DNS type host.
Using confcli:
[user@acd-router ~]# confcli services.routing.hostGroups -w
Running wizard for resource 'hostGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
hostGroups : [
hostGroup can be one of
1: dns
2: host
3: redirecting
Choose element index or name: dns
Adding a 'dns' element
hostGroup : {
name (default: ): edns-cdn
type (default: dns): ⏎
hosts : [
host : {
name (default: ): edns-host
hostname (default: ): ednshost.com
ipv6_address (default: ): ⏎
}
Add another 'host' element to array 'hosts'? [y/N]: ⏎
]
}
Add another 'hostGroup' element to array 'hostGroups'? [y/N]: ⏎
]
Generated config:
{
"hostGroups": [
{
"name": "edns-cdn",
"type": "dns",
"hosts": [
{
"name": "edns-host",
"hostname": "ednshost.com",
"ipv6_address": ""
}
]
}
]
}
Merge and apply the config? [y/n]: y
Note that the hostname is the name of the DNS server that will receive
the EDNS request. You can now use the DNS host when creating the routing tree,
e.g. in a random type rule:
[user@router ~]# confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: random
Adding a 'random' element
rule : {
name (default: ): random-rule
type (default: random): ⏎
targets : [
target (default: ): edns-host
Add another 'target' element to array 'targets'? [y/N]: ⏎
]
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "random-rule",
"type": "random",
"targets": [
"edns-host"
]
}
]
}
Merge and apply the config? [y/n]: y
[user@router ~]# confcli services.routing.entrypoint random-rule
services.routing.entrypoint = 'random-rule'
Or simply use it as the entrypoint:
confcli services.routing.entrypoint edns-host
8 - How to use ESB3024 Router with Orbit Request Router
This page describes how to write configuration for using ESB3024 Router in conjunction with ESB3008 Request Router. It can request a host from an ESB3008 Request Router and forward the response to the client, perform load balancing between several Request Routers and handle fallback to an external CDN in case no Request Routers are able to service the request.
Simple Example
# The tuning parameter `target.requestAttempts` controls the number of attempts
# made to find a host. In this example we want to fallback to the external CDN
# in case both Request Routers are down, so we set it to at least 3.
$ confcli services.routing.tuning.target.requestAttempts 3
services.routing.tuning.target.requestAttempts = 3
# Create a classifier to filter incoming requests from Sweden.
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: geoip
Adding a 'geoip' element
classifier : {
name (default: ): sweden_classifier
type (default: geoip): ⏎
inverted (default: False): ⏎
continent (default: ): ⏎
country (default: ): sweden
cities : [
city (default: ): ⏎
Add another 'city' element to array 'cities'? [y/N]: ⏎
]
asn (default: ): ⏎
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "sweden_classifier",
"type": "geoip",
"inverted": false,
"continent": "",
"country": "sweden",
"cities": [
""
],
"asn": ""
}
]
}
Merge and apply the config? [y/n]: y
# Create a session group for the non-Swedish requests, to use in routing rules
$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): sweden
classifiers : [
classifier (default: ): sweden_classifier
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "sweden",
"classifiers": [
"sweden_classifier"
]
}
]
}
Merge and apply the config? [y/n]: y
# The internal CDN uses Orbit Request Routers that return a redirect
# location. The router takes the content from the client request,
# and makes its own request to the Request Router using that same
# content. The returned location is then forwarded to the client.
#
# In case the Request Router responds with an error code, or times out,
# the router will continue traversing the routing tree until a
# successful route is found or the entire tree has been evaluated.
$ confcli services.routing.hostGroups -w
Running wizard for resource 'hostGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
hostGroups : [
hostGroup can be one of
1: dns
2: host
3: redirecting
Choose element index or name: redirecting
Adding a 'redirecting' element
hostGroup : {
name (default: ): internal-cdn
type (default: redirecting): ⏎
httpPort (default: 80): ⏎
httpsPort (default: 443): ⏎
headersToForward <A list of HTTP headers to forward to the CDN. (default: [])>: [
headersToForward (default: ): ⏎
Add another 'headersToForward' element to array 'headersToForward'? [y/N]: ⏎
]
allowAnyRedirectType (default: False): ⏎
hosts : [
host : {
name (default: ): internal-host-1
hostname (default: ): internal-host-1.example.com
ipv6_address (default: ): ⏎
}
Add another 'host' element to array 'hosts'? [y/N]: y
host : {
name (default: ): internal-host-2
hostname (default: ): internal-host-2.example.com
ipv6_address (default: ): ⏎
}
Add another 'host' element to array 'hosts'? [y/N]: ⏎
]
}
Add another 'hostGroup' element to array 'hostGroups'? [y/N]: ⏎
]
Generated config:
{
"hostGroups": [
{
"name": "internal-cdn",
"type": "redirecting",
"httpPort": 80,
"httpsPort": 443,
"headersToForward": [],
"allowAnyRedirectType": false,
"hosts": [
{
"name": "internal-host-1",
"hostname": "internal-host-1.example.com",
"ipv6_address": ""
},
{
"name": "internal-host-2",
"hostname": "internal-host-2.example.com",
"ipv6_address": ""
}
]
}
]
}
Merge and apply the config? [y/n]: y
# The offload CDN is non-redirecting, meaning that router does not
# make any request to its associated hosts to ask for a redirect location
# to forward to the clients, instead it simply returns a location using
# one of the CDN's hosts substituted for the router's host in the
# client request possibly with some kind of respsonse translation to add
# e.g. tokens or path prefixes.
$ confcli services.routing.hostGroups -w
Running wizard for resource 'hostGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
hostGroups : [
hostGroup can be one of
1: dns
2: host
3: redirecting
Choose element index or name: host
Adding a 'host' element
hostGroup : {
name (default: ): offload-cdn
type (default: host): ⏎
httpPort (default: 80): ⏎
httpsPort (default: 443): ⏎
hosts : [
host : {
name (default: ): offload-host
hostname (default: ): offload-host.example.com
ipv6_address (default: ): ⏎
}
Add another 'host' element to array 'hosts'? [y/N]: ⏎
]
}
Add another 'hostGroup' element to array 'hostGroups'? [y/N]: ⏎
]
Generated config:
{
"hostGroups": [
{
"name": "offload-cdn",
"type": "host",
"httpPort": 80,
"httpsPort": 443,
"hosts": [
{
"name": "offload-host",
"hostname": "offload-host.example.com",
"ipv6_address": ""
}
]
}
]
}
Merge and apply the config? [y/n]: y
# Begin with adding a load balancing route rule between the two internal
# hosts. Let's make internal-host-2 have twice the capacity of
# internal-host-1 by using a weighted rule.
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: weighted
Adding a 'weighted' element
rule : {
name (default: ): balancer
type (default: weighted): ⏎
targets : [
target : {
target (default: ): internal-host-1
weight (default: 100): 1,
rule (default: always()): always()
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
target (default: ): internal-host-2
weight (default: 100): 2,
rule (default: always()): always()
}
Add another 'target' element to array 'targets'? [y/N]: ⏎
]
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "balancer",
"type": "weighted",
"targets": [
{
"target": "internal-host-1",
"weight": "1",
"rule:": "always()"
},
{
"target": "internal-host-2",
"weight": "2",
"rule:": "always()"
}
]
}
]
}
Merge and apply the config? [y/n]: y
# Make an offload rule to route any traffic not in Sweden to the
# offload CDN.
$ confcli services.routing.rules -w
Running wizard for resource 'rules'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
rules : [
rule can be one of
1: allow
2: consistentHashing
3: contentPopularity
4: deny
5: firstMatch
6: random
7: rawGroup
8: rawHost
9: split
10: weighted
Choose element index or name: firstMatch
Adding a 'firstMatch' element
rule : {
name (default: ): offload
type (default: firstMatch): ⏎
targets : [
target : {
onMatch (default: ): offload-host
rule (default: ): not in_session_group('sweden')
}
Add another 'target' element to array 'targets'? [y/N]: y
target : {
onMatch (default: ): balancer
rule (default: ): in_session_group('sweden')
}
Add another 'target' element to array 'targets'? [y/N]: ⏎
]
onMiss (default: ): offload-host
}
Add another 'rule' element to array 'rules'? [y/N]: ⏎
]
Generated config:
{
"rules": [
{
"name": "offload",
"type": "firstMatch",
"targets": [
{
"onMatch": "offload-host",
"condition": "not in_session_group('sweden')"
},
{
"onMatch": "balancer",
"condition": "in_session_group('sweden')"
}
],
"onMiss": "offload-host"
}
]
}
Merge and apply the config? [y/n]: y{
"tuning": {
// "target_request_attempts" controls the number of attempts to find a host.
// In this example we want to fallback to the external CDN in case both
// Request Routers are down, so we set it to 3.
"target_request_attempts": 3
},
"session_groups": [
{
"id": 1,
"name": "not_sweden",
"classifiers": [
[
{
"id": 1,
"inverted": true,
"name": "not_sweden_classifier",
"rule": {
"rule_type": "geoip_rule",
"source": "session/client_ip",
"country": "Sweden"
}
}
]
]
}
],
"cdns": [
{
"id": "internal-cdn",
"http_port": 80,
"https_port": 443,
// The internal CDN uses Orbit Request Routers that return a redirect
// location. The router takes the content from the client request,
// and makes its own request to the Request Router using that same
// content. The returned location is then forwarded to the client.
//
// In case the Request Router responds with an error code, or times out,
// the router will continue traversing the routing tree until a
// successful route is found or the entire tree has been evaluated.
"redirecting": true,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
},
{
"id": "offload-cdn",
"http_port": 80,
"https_port": 443,
// The offload CDN is non-redirecting, meaning that router does not
// make any request to its associated hosts to ask for a redirect location
// to forward to the clients, instead it simply returns a location using
// one of the CDN's hosts substituted for the router's host in the
// client request possibly with some kind of respsonse translation to add
// e.g. tokens or path prefixes.
"redirecting": false,
"manifest_availability_check": {
"enabled": false,
"session_group_ids": []
}
}
],
"hosts": [
{
"id": "internal-host-1",
"cdn_id": "internal-cdn",
"address_family": "ipv4",
"host": "internal-host-1.example"
},
{
"id": "internal-host-2",
"cdn_id": "internal-cdn",
"address_family": "ipv4",
"host": "internal-host-2.example"
},
{
"id": "offload-host",
"cdn_id": "offload-cdn",
"address_family": "ipv4",
"host": "offload-host.example"
}
],
"routing": {
"id": "routing_table",
// "sequential" - Go through the rules at this level one by one, and pick
// the first that returns a positive weight.
"member_order": "sequential",
"members": [
{
"id": "reject-node",
"host_id": "offload-host",
// Send filtered-out clients to an offload host.
"weight_function": "return session_groups.not_sweden and 1 or 0"
},
{
"id": "internal_routing",
// Return 1 to make sure this tree is evaluated at all.
"weight_function": "return 1"
// "weighted" - Evaluate all the rules at this level, and pick one of
// them randomly based on their respective weights.
//
// In this example host 1 has half the capacity of host 2, so we just
// randomly route twice as many clients to host 2.
"member_order": "weighted",
"members": [
{
"id": "internal-node-1",
"host_id": "internal-host-1",
// Half the weight of the other host -> half as likely to be picked.
"weight_function": "return 1"
},
{
"id": "internal-node-2",
"host_id": "internal-host-2",
// Twice the weight of the other -> twice as likely to be picked.
"weight_function": "return 2"
}
]
},
{
// Add a non-redirecting offload host as the last target in case all the
// internal redirecting hosts are unavailable or respond with some kind
// of error code.
"id": "offload-node",
"host_id": "offload-host",
"weight_function": "return 1"
}
]
}
}9 - How to use ESB3024 Router with CoreDNS
CoreDNS can be configured to work as a DNS server in front of ESB3024 Router. In this mode of operation the client DNS request is terminated by CoreDNS which requests an optimal streaming location from the router.
Contact AgileTV for detailed information on how to set this up, if you don’t already have an AgileTV CDN Solutions Support account please contact us here.
10 - How to use the Director as a replacement for Orbit Request Router
This guide outlines how to replicate Convoy’s ESB3008 HTTP Request Router using the Director. This is accomplished using the Convoy Bridge, which is an integration service designed to allow the router and an existing Convoy installation to work together seamlessly.
Following the steps outlined below enables the replacement of existing ESB3008 HTTP Request Router instances with the Director. As of the current documentation, the Director is not a direct substitute for the ESB3008 HTTP Request Router. However, most of the functionality is available, and in the majority of cases, the Director can be used without any loss of functionality.
The purpose of this guide is to detail the process of transitioning from an existing Convoy installation, which includes one or more instances of ESB3008 HTTP Request Router, to a configuration utilizing the Director. This transition allows the existing Convoy installation to continue its role in CDN provisioning, content management, and analytics. Simultaneously, the router assumes responsibility for managing HTTP(S) redirects directly from clients to selected streamers.
One functional difference between ESB3008 HTTP Request Router and the Director is
in communication with the streamer to determine specific interface assignments.
ESB3008 uses a proprietary protocol to communicate with the streamer to help determine
the best interface for the client. For the Director, in order to work with both
streamers and third-party CDNs, the use of the proprietary protocol is not supported,
and the router’s rule engine must be used to accomplish the same result. It is for
this reason that it is recommended to configure each streaming interface as a distinct
host entry in the router’s configuration. This allows the router to address each
interface separately.
Prerequisites
This guide assumes both an existing Convoy installation and a partiality configured
instance of the Director. The router should have all the necessary routing configuration
in place for hostGroups, sessionGroups and routing rules to direct traffic
to the streaming interfaces of the streamers. It is also assumed that the proper
firewall rules are allowing the required traffic between the router, the Convoy
Bridge integration and the Convoy management nodes.
The minimum compatible version of Convoy which can be used with the analytics synchronization feature is Convoy 3.0.0. Previous versions may be used only if Convoy is not being used for analytics.
Firewall Configuration
Proper firewall configuration is required to allow both the router and Convoy to communicate through bidirectional connections established from the Convoy Bridge integration. The Convoy Bridge does not need to be enabled for each router instance, one instance of the Convoy Bridge can synchronize multiple instances of the router simultaneously. Only the router nodes with the Convoy Bridge enabled will need access through the firewall to establish outgoing connections to the Convoy management nodes, or whichever nodes are running MariaDB and Kafka. Additionally, if multiple router instances are to be synchronized by a single Convoy Bridge instance, the firewall must allow the Convoy Bridge to establish connections to each router instance on port 5001.
The following table outlines the necessary connections to be allowed through the firewall.
| Source | Destination | Port | Description |
|---|---|---|---|
| Convoy Bridge | Convoy Management | 3306/TCP | Account Configuration |
| Convoy Bridge | Convoy Management | 9092/TCP | Analytics Data |
| Convoy Bridge | The router | 5001/TCP | Rest API |
Response Translation Functions
In order to have the router generate a redirect URL which is compatible with the
format generated by the ESB3008 HTTP Request Router, a response translation function
must be used to modify the redirect URL before it is returned to the client. A built-in
function convoy_compatibility_response is available to be
used for this purpose. This function appends both the session-id and storage-prefix
based on the account and distribution information in Convoy.
On the Director, set the response translation function as follows:
> confcli services.routing.translationFunctions.response "return convoy_compatibility_response(Headers, '')"
This function depends on the account synchronization feature of the Convoy bridge to
determine the correct storage prefix based on the incoming Host header value. It works
by using the Host header to lookup the storage prefix from the account configuration,
and then modifying the redirect URL to prepend /session/<session-id>/<prefix>. The
streamer can then use this information for both origin selection and session tracking.
Enabling the Convoy Bridge
Before using the Convoy Bridge integration, a valid configuration must first be applied.
All configuration for the Convoy Bridge is available in confd under the key
integration.convoy.bridge. The configuration is divided into three main sections:
The account synchronization feature, the convoy analytics integration, and a list of
additional router instances to synchronize.
By default, the Convoy Bridge will include the current local router instance in the synchronization process. The connection details are hard-wired into the container at startup, and do not need to be specified in the configuration. Because one Convoy Bridge can serve multiple instances of the router, it is not necessary to configure the Convoy Bridge on each node separately. However, if multiple Convoy Bridge instances are used, it is recommended to configure them so that one router is served by multiple instances of the Convoy Bridge. This provides high-availability in a production environment.
As an example, if there are 4 router instances, A, B, C, and D, and 4 Convoy Bridge instances, configuring them such that Bridge 1 synchronizes A & B, 2, synchronizes B & C, 3, synchronizes C & D and 4 synchronizes D & A will provide fault tolerance in the event that one bridge is unavailable.
The account synchronization feature of the Convoy Bridge watches the MariaDB database on the
Convoy management node for changes to the account and distribution configuration, and if
the configuration differs from what is currently known to the router the configuration
will be updated. This feature is required by both the router for use with the
convoy_compatibility_response response translation function, and within the Convoy
Bridge integration for setting the correct account, storage, and distribution fields
with the analytics integration feature.
The analytics integration feature monitors monitors the router’s Event API for
newly established sessions, and produces session-record messages over the Kafka
message broker for use by Convoy. This feature requires that Convoy is both licensed
and configured for use with the analytics feature. The integration works by establishing
a persistent connection to each router, and listening for new session events. Due to the
protocol in use by the Event API to send the events, only one instance of the Convoy
Bridge will have the ability to receive each event, and only that instance will produce
the session-record to the Kafka message brokers.
Synchrnoizing multiple routers with the same instance of the Convoy Bridge is performed
by configuring the otherRouters section of the Convoy Bridge configuration. In addition
to the local router, each otherRouter entry will have the same account and distribution
information updated simultaneously, and will be used as an event source for the analytics
integration. The otherRouters entries require the URL to the router’s Rest API, an
optional API key, and a flag to indicate if SSL certificate validation should be enforced.
Enable the account synchronization feature by running the following command:
> confcli integration.convoy.bridge.accounts -w
Running wizard for resource 'accounts'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
accounts : {
enabled (default: False): true
dbUrl (default: mysql://user:pass@localhost:3306): mysql://convoy:password@convoy-mgmt1:3306
cmsUrl (default: http://localhost:5555): http://convoy-mgmt1:5555
pollInterval (default: 60):
}
Generated config:
{
"accounts": {
"enabled": true,
"dbUrl": "mysql://convoy:password@convoy-mgmt1:3306",
"cmsUrl": "http://convoy-mgmt1:5555",
"pollInterval": 60
}
}
Merge and apply the config? [y/n]:
This will result in the Convoy Bridge continuously polling the Convoy instance running on
convoy-mgmt1 every 60 seconds for changes to the account configuration. Only when the
account configuration differs from the current configuration on the router will it be
updated with the change.
The pollInterval parameter can be used to control how frequently the Convoy Bridge will
attempt to poll the database for changes. Setting this value too high will result in a longer delay
between changes being made in Convoy and the changes being reflected in the router. Setting this
value too low will result in unnecessary load on the Convoy database. The default value of 60 seconds
should be sufficient for most use cases.
To enable the analytics integration feature, run the following command:
> confcli integration.convoy.bridge.analytics -w
Running wizard for resource 'analytics'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
analytics : {
enabled (default: False): true
brokers : [
broker (default: ): convoy-mgmt1:9092
Add another 'broker' element to array 'brokers'? [y/N]: y
broker (default: ): convoy-mgmt2:9092
Add another 'broker' element to array 'brokers'? [y/N]: y
broker (default: ): convoy-mgmt3:9092
Add another 'broker' element to array 'brokers'? [y/N]: n
]
batchInterval (default: 10):
maxBatchSize (default: 500):
}
Generated config:
{
"analytics": {
"enabled": true,
"brokers": [
"convoy-mgmt1:9092",
"convoy-mgmt2:9092",
"convoy-mgmt3:9092"
],
"batchInterval": 10,
"maxBatchSize": 500
}
}
Merge and apply the config? [y/n]:
This will enable the analytics integration, which will produce session-record messages
to the configured Kafka brokers. The brokers configuration list is used to bootstrap the
initial set of brokers which will be contacted by the Convoy Bridge to then obtain the
current set of active brokers. It is not necessary to include all brokers here, as the
initial connection will automatically negotiate the correct broker list.
The batchInterval parameter must be set to a value much less than 60 seconds, since the
streamer will send session-sample messages to Kafka every 60 seconds, and if the
corresponding session-record message is not received within that time, the sample will
be dropped, resulting in potentially inaccurate bandwidth statistics.
> confcli integration.convoy.bridge.otherRouters -w
Running wizard for resource 'otherRouters'
<Other routers which will be kept in sync (default: [])>
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
otherRouters <Other routers which will be kept in sync (default: [])>: [
otherRouter : {
url (default: ): https://router-2:5001
apiKey (default: ): 1234
validateCerts (default: True): false
}
Add another 'otherRouter' element to array 'otherRouters'? [y/N]: y
otherRouter : {
url (default: ): https://router-3:5001
apiKey (default: ):
validateCerts (default: True):
}
Add another 'otherRouter' element to array 'otherRouters'? [y/N]: n
]
Generated config:
{
"otherRouters": [
{
"url": "https://router-2:5001",
"apiKey": "1234",
"validateCerts": false
},
{
"url": "https://router-3:5001",
"apiKey": "",
"validateCerts": true
}
]
}
Merge and apply the config? [y/n]:
11 - Route on Internally-Tracked Quality of Experience (QoE)
This page describes how to configure QoE-based routing. For background on In-Stream sessions (required for QoE tracking), see Use In-Stream Sessions. For configuration in general, see Configuration.
How QoE Tracking Works
The router tracks the quality of each In-Stream session by observing which video bitrate variant the client requests on every segment fetch. Quality is measured on a scale of 1–4, where 1 is the lowest bitrate variant and 4 is the highest.
Note: A low quality reading may reflect a mobile device whose highest useful bitrate is low — it does not necessarily indicate that the CDN is performing poorly.
These observations are aggregated per host per session group into a single
score in the range [0.0, 1.0]:
score = (0 × count_q1 + 1 × count_q2 + 2 × count_q3 + 4 × count_q4) / (4 × total_count)
Quality levels are weighted exponentially (factors 0, 1, 2, 4) to give extra weight to the highest quality tier.
Score Decay
Scores decay exponentially over time so that stale observations lose influence.
The decayFactor setting is applied per second; internally the decay fires
every 5 seconds, so the factor applied per tick is decayFactor^5. For
example, decayFactor = 0.99 results in roughly 5% decay every 5 seconds
(~40% per minute).
The counter for the highest quality level (q4) never decays below 1.0.
This acts as an optimistic prior: a host with no recent traffic converges
toward a score of 1.0, encouraging the system to send some traffic there to
build up fresh statistics.
Enabling QoE Tracking
QoE tracking is enabled per session group in
services.routing.settings.qoeTracking:
$ confcli services.routing.settings.qoeTracking.enabled true
services.routing.settings.qoeTracking.enabled = True
$ confcli services.routing.settings.qoeTracking.decayFactor 0.99
services.routing.settings.qoeTracking.decayFactor = 0.99
# Use the wizard to add session group names "my_group" and "my_group_agents"
$ confcli services.routing.settings.qoeTracking.sessionGroupNames -w
sessionGroupNames: my_group
sessionGroupNames: my_group_agents
enabled: Master switch. Iffalse, no scores are collected for any group.decayFactor: Per-second exponential decay (applied every 5 s asdecayFactor^5).sessionGroupNames: Session groups for which QoE is tracked. Each group must also be defined inservices.routing.sessionGroups. If left empty, all session groups are tracked.requestsIgnoredAfterHostChange(optional, default0): Number of segment requests to skip after a session moves to a new host before incrementing QoE counters. Prevents a burst of potentially stale quality readings from polluting the new host’s score.
Regular and Agent Sessions
An agent is a session that stays pinned to its assigned host even when it experiences bad quality. Agents ensure the router always has observations on every host — including hosts that are currently degraded. Without agents, a poor host would quickly lose all traffic and the router would never learn that it had recovered.
Agents are identified by any
classifier.
In the examples below, agents are identified by a URL query parameter
(agent=1), but any classifier that isolates a small, stable subset of
sessions works. The naming convention <group>_agents for the agent session
group is expected by the host_weight routing function (see below).
By contrast, a regular session is allowed to move between hosts based on its current quality of experience.
Use Cases
QoE routing supports two distinct configurations depending on whether sessions need to be dynamically re-routed mid-stream.
| QoE InStream Selection | QoE Initial Selection | QoE Monitoring Only | |
|---|---|---|---|
| Regular sessions | In-Stream (router proxies manifests, redirects each segment) | Initial selection only (single 302 redirect at stream start) | Initial selection without QoE |
| Agent sessions | In-Stream, pinned to host | In-Stream, pinned to host | In-Stream, small fraction, not pinned to host |
| Routing function for regular sessions | host_weight (re-evaluated per request) | agent_weight (evaluated once at stream start) | N/A |
| Can move regular sessions mid-stream | Yes | No | N/A |
| Router load | Higher | Lower | Lower |
| Eager switching reqired | Yes | Yes | No |
Routing Functions
The following Lua functions are used in routing rule weight expressions.
agent_weight(session_group, default_weight, amplifier [, fallback_hosts])
Used in routing rules for agent sessions. Agents stay pinned to their assigned host regardless of quality.
| Situation | Weight returned |
|---|---|
| No session yet (pre-assignment) | default_weight |
| Managed (In-Stream) session on same host as this rule | 10000 (retain) |
| Managed session on a different host (not a fallback) | 0 (excluded) |
Managed session on a different host that is in fallback_hosts | 1 (allow escape from fallback) |
| Unmanaged (initial selection) session | QoE score for this host |
The optional fallback_hosts list prevents agents from being permanently
stuck on a fallback host. If an agent’s current host is in fallback_hosts,
primary hosts receive a weight of 1, allowing the agent to move back and
resume generating useful statistics.
Parameters:
session_group— The agent session group whose QoE scores are used for initial placement as a fallback if all in-stream sessions are used and the session can’t be a managed session.default_weight— Weight used when no session exists yet and no QoE data is available, providing initial even balancing across hosts.amplifier— Controls how strongly QoE scores influence the weight. Can be a number or the name of a selection input variable. With amplifier2, a host at score1.0receives weight10010vs10for score0.0— a 1000× difference.fallback_hosts(optional) — List of host IDs considered fallback hosts.
host_weight(session_group, amplifier [, initial_requests_skipped])
Used in routing rules for regular (non-agent) sessions. Sessions prefer to
stay on their current host when quality is good, but are likely to move when
quality is bad. Routing decisions are based on the QoE scores of the
corresponding agent group (session_group .. "_agents"), not the regular
sessions themselves.
Decision logic:
No session or no assigned host — return QoE score for this host (drawn from the
_agentsgroup).Within the initial lock period (
requests_since_last_host_change <= initial_requests_skipped) — lock to the current host. This prevents flip-flopping immediately after a host change while the client may still be playing buffered content from the previous host.Past the initial lock period — draw a random number and compare against a stay probability based on current quality:
Quality Stay probability Expected requests before move ≤ 2 (bad) 0.0833 (~1/12) ~12 (~12 s at 1 req/s) > 2 (good) 0.995 ~200 (~3 min at 1 req/s) - If staying: return
10000for the current host,1for others. - If moving: return the QoE score for each host, letting the routing algorithm select the best available host.
- If staying: return
Parameters:
session_group— The regular session group. Scores are read fromsession_group .. "_agents".amplifier— Same meaning as inagent_weight. Use a lower value (e.g.1) than for agents since regular sessions are re-evaluated on every request.initial_requests_skipped(optional, default0) — Number of requests after a host change during which the session is locked to its host.
Configuration: QoE InStream Selection
All sessions run as In-Stream. The router observes every segment request and can move regular sessions mid-stream when quality degrades.
1. Classifiers and session groups
Identify agents using any classifier. This example uses a URL query parameter:
$ confcli services.routing.classifiers -w
...
name: is_agent
type: contentUrlQueryParameters
patternType: stringMatch
pattern: agent=1
$ confcli services.routing.sessionGroups -w
...
# Agent group
name: my_group_agents
classifiers: ["is_agent"]
# Regular group (everyone who is not an agent)
name: my_group
classifiers: ["not 'is_agent'"]
2. QoE tracking
$ confcli services.routing.settings.qoeTracking.enabled true
$ confcli services.routing.settings.qoeTracking.decayFactor 0.99
$ confcli services.routing.settings.qoeTracking.sessionGroupNames -w
sessionGroupNames: "my_group"
sessionGroupNames: "my_group_agents"
Both groups are tracked. Scores for my_group_agents drive routing decisions
via host_weight. Scores for my_group are available for monitoring in
Grafana and may in future be incorporated into routing decisions.
3. Enable In-Stream for all sessions
$ confcli services.routing.translationFunctions.session "return set_session_type('instream')"
services.routing.translationFunctions.session = "return set_session_type('instream')"
See Session Translation Function for details.
Set the services.routing.tuning.general.maxActiveManagedSessions value to a higher
limit in order to ensure enough in-stream sessions can be created.
4. Enable eager CDN switching
$ confcli services.routing.tuning.general.eagerCdnSwitching true
services.routing.tuning.general.eagerCdnSwitching = True
5. Routing rules
A split rule sends agents and regular sessions to separate weighted rules:
{
"rules": [
{
"name": "agent_or_regular",
"type": "split",
"condition": "in_session_group('my_group_agents')",
"onMatch": "agent_weights",
"onMiss": "regular_weights"
},
{
"name": "regular_weights",
"type": "weighted",
"targets": [
{ "target": "host1", "weight": "host_weight('my_group', 1)", "condition": "always()" },
{ "target": "host2", "weight": "host_weight('my_group', 1)", "condition": "always()" }
]
},
{
"name": "agent_weights",
"type": "weighted",
"targets": [
{ "target": "host1", "weight": "agent_weight('my_group_agents', 100, 2.0, {'host2'})", "condition": "always()" },
{ "target": "host2", "weight": "agent_weight('my_group_agents', 100, 2.0, {'host1'})", "condition": "always()" }
]
}
]
}
Note the amplifier is 1 for host_weight (regular sessions) and 2 for
agent_weight. Regular sessions are re-evaluated on every segment request so
even a modest preference for better hosts moves traffic effectively. Agents
only go through initial QoE-based placement once, so a stronger preference is
appropriate.
$ confcli services.routing.entrypoint agent_or_regular
services.routing.entrypoint = 'agent_or_regular'
Configuration: QoE Initial Selection
Only agents run as In-Stream. Regular sessions receive a single CDN selection at stream start, chosen using the QoE scores the agents have accumulated. Regular sessions cannot be re-routed mid-stream.
In this mode a single agent_weight rule handles both agents (which stay on
their assigned host) and regular sessions (which receive a one-time QoE-scored
placement). No separate host_weight rule is needed.
Agents do not need a separate classifier — a fraction of sessions in the target
group is automatically promoted to In-Stream by managedSessions.
1. Session groups
A single session group covers all sessions:
$ confcli services.routing.sessionGroups -w
...
name: qoe_initial
classifiers: ["all"]
2. QoE tracking
$ confcli services.routing.settings.qoeTracking.enabled true
$ confcli services.routing.settings.qoeTracking.decayFactor 0.99
$ confcli services.routing.settings.qoeTracking.sessionGroupNames '["qoe_initial"]'
3. Make a fraction of sessions In-Stream (agents)
Use managedSessions to promote a fraction of sessions to In-Stream. These
become the agents. The rest remain as initial-selection sessions.
$ confcli services.routing.settings.managedSessions.fraction 0.1
services.routing.settings.managedSessions.fraction = 0.1
$ confcli services.routing.settings.managedSessions.sessionTypes -w
...
type: instream
enabled: true
sessionGroupNames: ["qoe_initial"]
maxActive: 1000
Keep the agent fraction small (5–10% is a reasonable starting point). Agents are pinned to their host even under poor quality conditions, so a larger fraction increases viewer exposure to any degraded host.
Do not set translationFunctions.session to instream here — that would
override the managedSessions fraction and make all sessions In-Stream.
4. Enable eager CDN switching
$ confcli services.routing.tuning.general.eagerCdnSwitching true
services.routing.tuning.general.eagerCdnSwitching = True
5. Routing rules
One weighted rule handles both agents and regular sessions:
{
"rules": [
{
"name": "qoe_initial_weights",
"type": "weighted",
"targets": [
{
"target": "host1",
"weight": "agent_weight('qoe_initial', 100, 2)",
"condition": "in_session_group('qoe_initial')"
},
{
"target": "host2",
"weight": "agent_weight('qoe_initial', 100, 2)",
"condition": "in_session_group('qoe_initial')"
}
]
}
]
}
The amplifier is set higher (2) than in the InStream example because
initial placement happens only once per session — a stronger preference for
high-quality hosts is needed to move traffic effectively.
$ confcli services.routing.entrypoint qoe_initial_weights
services.routing.entrypoint = 'qoe_initial_weights'
Monitoring
If Grafana is installed together with the ESB-3024 installer, the “Quality of Experience” dashboard provides insight into QoE scores per host and session group, including current score values and session movement over time.
Notes on Performance
QoE routing requires In-Stream sessions for at least a subset of traffic (all sessions in the InStream configuration, agents only in the Initial Selection configuration). In-Stream sessions put additional load on the router since clients return to the router for every manifest and segment redirect. See Use In-Stream Sessions for further performance guidance.
12 - Use In-Stream Sessions
In-Stream sessions is a routing mode where the router keeps control of the session for the full session lifetime instead of only routing the session to the preferred CDN (or cache node) once. Technically it works by letting the client return to the router for manifest and segments and redirect each segment to the preferred CDN. This enables changing CDN in the middle of a session.
Configure In-Stream Sessions
In-Stream routing for all incoming requests can be enabled by running
$ confcli services.routing.translationFunctions.session "return set_session_type('instream')"
services.routing.translationFunctions.session = "return set_session_type('instream')"
See Session Translation Function for more details on controlling session types.
The following example illustrate the request flow for in-stream session
$ curl -i http://test-acd-router/content/playlist.m3u8
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Content-Length: 0
Location: http://test-acd-router/__s/test-acd-router-a4fd86-0000004f_WyIvY2RuL3BsYXlsaXN0Lm0zdTgiLDAsImRldmNkbjEiLCJkZXZjZG4xLnN0cmVhbXBpbG90LnR2Iiw4MCwiL2Nkbi9wbGF5bGlzdC5tM3U4IiwwLDE2ODAyNjQwOTNd_c1e3fc663a47156e0894b30eddf995bb/content/playlist.m3u8
X-Service-Identity:
$ curl -i http://test-acd-router/__s/test-acd-router-a4fd86-0000004f_WyIvY2RuL3BsYXlsaXN0Lm0zdTgiLDAsImRldmNkbjEiLCJkZXZjZG4xLnN0cmVhbXBpbG90LnR2Iiw4MCwiL2Nkbi9wbGF5bGlzdC5tM3U4IiwwLDE2ODAyNjQwOTNd_c1e3fc663a47156e0894b30eddf995bb/content/playlist.m3u8
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length,Content-Range
Connection: keep-alive
Content-Length: 306
Content-Type: application/vnd.apple.mpegurl
Date: Fri, 31 Mar 2023 10:18:42 GMT
ETag: "5d711f08-132"
Last-Modified: Thu, 05 Sep 2019 14:43:20 GMT
Server: nginx/1.14.0 (Ubuntu)
X-Service-Identity:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=500000,RESOLUTION=320x180
abr_500k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=640x360
abr_1000k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1700000,RESOLUTION=1280x720
abr_1700k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3300000,RESOLUTION=1280x720
abr_3300k.m3u8
$ curl -i http://test-acd-router/__s/test-acd-router-a4fd86-0000004f_WyIvY2RuL3BsYXlsaXN0Lm0zdTgiLDAsImRldmNkbjEiLCJkZXZjZG4xLnN0cmVhbXBpbG90LnR2Iiw4MCwiL2Nkbi9wbGF5bGlzdC5tM3U4IiwwLDE2ODAyNjQwOTNd_c1e3fc663a47156e0894b30eddf995bb/content/abr_500k.m3u8
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length,Content-Range
Connection: keep-alive
Content-Length: 276
Content-Type: application/vnd.apple.mpegurl
Date: Fri, 31 Mar 2023 12:02:04 GMT
ETag: "6426cbbc-114"
Last-Modified: Fri, 31 Mar 2023 12:02:04 GMT
Server: nginx/1.14.0 (Ubuntu)
X-Service-Identity:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:1
#EXT-X-MEDIA-SEQUENCE:112583846
#EXTINF:0.834444,
abr_500k112583846.ts
#EXTINF:0.834444,
abr_500k112583847.ts
#EXTINF:0.834444,
abr_500k112583848.ts
#EXTINF:0.834444,
abr_500k112583849.ts
#EXTINF:0.834444,
abr_500k112583850.ts
$ curl -i http://test-acd-router/__s/test-acd-router-a4fd86-0000004f_WyIvY2RuL3BsYXlsaXN0Lm0zdTgiLDAsImRldmNkbjEiLCJkZXZjZG4xLnN0cmVhbXBpbG90LnR2Iiw4MCwiL2Nkbi9wbGF5bGlzdC5tM3U4IiwwLDE2ODAyNjQwOTNd_c1e3fc663a47156e0894b30eddf995bb/content/abr_500k112583846.ts
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Content-Length: 0
Location: http://testcdn.tv/abr_500k112583846.ts
X-Service-Identity:
A Note on Confcli
Begin with configuring routing, classification and session groups in confcli, for example based on content types.
# Create a classifier for live sessions
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
classifiers : [
classifier can be one of
1: anonymousIp
2: asnIds
3: contentUrlPath
4: contentUrlQueryParameters
5: geoip
6: hostName
7: ipranges
8: random
9: regexMatcher
10: requestHeader
11: stringMatcher
12: subnet
13: userAgent
Choose element index or name: contentUrlPath
Adding a 'contentUrlPath' element
classifier : {
name (default: ): live
type (default: contentUrlPath): ⏎
inverted (default: False): ⏎
patternType (default: stringMatch): ⏎
pattern (default: ): *live*
}
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
"classifiers": [
{
"name": "live",
"type": "contentUrlPath",
"inverted": false,
"patternType": "stringMatch",
"pattern": "*live*"
}
]
}
Merge and apply the config? [y/n]: y
# Create a session group called "live" using the classifier
$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'
Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string
sessionGroups : [
sessionGroup : {
name (default: ): live
classifiers : [
classifier (default: ): live
Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
}
Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
"sessionGroups": [
{
"name": "live",
"classifiers": [
"live"
]
}
]
}
Merge and apply the config? [y/n]: y
# Make all sessions belonging to the session group "live" In-Stream sessions
$ confcli services.routing.translationFunctions.session "return set_session_type_if_in_group('instream', 'live')"
services.routing.translationFunctions.session = "return set_session_type_if_in_group('instream', 'live')"
Monitoring
If Grafana is installed together with the ESB-3024 installer, the number of started In-Stream sessions as well as currently active In-Stream sessions can be seen in a Dashboard.
Notes on Performance
Be aware that In-Stream sessions puts additional load on the request router since all clients continuously comes back to the router to fetch new manifests and to get segment redirects. The necessary router capacity therefore scaled both with number of started sessions and the average session length.