Flomesh-Bookinfo基于k8s的演示

在这个材料中,我们会演示bookinfo应用在k8s的部署以及工作原理。作为前提,阅读如下材料可以帮助理解本文档,但是如果没有这些背景信息,也可以一步一步跟随本文档完成演示:

如果已经完成了“Flomesh在k8s上的部署(推荐方案I)”,那么可以直接跳转到"部署bookinfo应用";如果还没有部署好环境,可以在如下两个选项中二选一:

  1. Flomesh在k8s上的部署(推荐方案I)
  2. Flomesh在k8s上的部署(推荐方案II)

这里我们简单对比下两种部署方式,#1是采用了外置的ipvs作为LoadBalancer,服务网关采用DaemonSet部署,对外采用LoadBalancer模式;#2是把服务网关只作为DaemonSet方式部署,并且网络选择HostPort模式。#1适合多租户,大规模部署,以及公有云等有外置LoadBalancer的情况;#2适合中小规模,偏向运维。在方案#2中,我们没有采用k8s的Service,因此相应的在网络链路上会减少iptables的使用(大多数的k8s部署采用iptables来做kube-proxy),相比#1具有更好的性能,遇到问题时候也更容易分析问题。

部署k8s环境-Hostport模式

准备镜像和yaml

我们首先下载需要的镜像和yaml文件:

wget http://flomesh.cn/k8s-express/aiur-alpine-0.2.0-43.tar 
wget http://flomesh.cn/k8s-express/bookinfo-reviews.tar      
wget http://flomesh.cn/k8s-express/kong-alpine-1.2.2-32.tar  
wget http://flomesh.cn/k8s-express/piped-alpine-0.1.0-59.tar
wget http://flomesh.cn/k8s-express/bookinfo-ratings.tar      
wget http://flomesh.cn/k8s-express/fort-alpine-0.2.0-43.tar  
wget http://flomesh.cn/k8s-express/mysql.tar                 
wget http://flomesh.cn/k8s-express/prometheus.tar

wget http://flomesh.cn/k8s-express/aiur.yaml  
wget http://flomesh.cn/k8s-express/bookinfo-ratings.yaml  
wget http://flomesh.cn/k8s-express/bookinfo-reviews.yaml  
wget http://flomesh.cn/k8s-express/fort.yaml  
wget http://flomesh.cn/k8s-express/kong.yaml  
wget http://flomesh.cn/k8s-express/mysql.yaml

简单起见,在安装好节点以后,我们在各个节点都把镜像导入:

ctr image import aiur-alpine-0.2.0-43.tar 
ctr image import bookinfo-reviews.tar      
ctr image import kong-alpine-1.2.2-32.tar  
ctr image import piped-alpine-0.1.0-59.tar
ctr image import bookinfo-ratings.tar      
ctr image import fort-alpine-0.2.0-43.tar  
ctr image import mysql.tar                 
ctr image import prometheus.tar

部署虚拟机,master和node

首先我们部署k8s环境,这里我们用k3s作为参考。步骤如下:

  1. 准备虚拟机
  2. 部署master节点
  3. 添加node节点

部署服务网关

接下来我们安装服务网关,这个环节和“部署方案I”有差异。我们采用如下的yaml来部署网关(为了解释方便,如下保留了行号):

      1 apiVersion: apps/v1
      2 kind: DaemonSet
      3 metadata:
      4   name: kong
      5   labels:
      6     app: kong
      7 spec:
      8   selector:
      9     matchLabels:
     10       app: kong
     11   template:
     12     metadata:
     13       labels:
     14         app: kong
     15     spec:
     16       initContainers:
     17       - name: kong-migration
     18         image: kong-alpine:1.2.2-32
     19         imagePullPolicy: IfNotPresent
     20         env:
     21           - name: KONG_DATABASE
     22             value: mysql
     23           - name: KONG_MYSQL_PASSWORD
     24             value: kong
     25           - name: KONG_MYSQL_HOST
     26             value: mysql.default.svc
     27           - name: KONG_MYSQL_DATABASE
     28             value: kong
     29           - name: KONG_PLUGINS
     30             value: bundled,aiur
     31         command: [ "kong", "migrations", "bootstrap" ]
     32 
     33       volumes:
     34       - name: piped-cm
     35         configMap:
     36           name: piped-cm
     37 
     38       containers:
     39       - name: kong
     40         image: kong-alpine:1.2.2-32
     41         imagePullPolicy: IfNotPresent
     42         env:
     43           - name: KONG_PLUGINS
     44             value: bundled,flomesh-bundled
     45           - name: KONG_DATABASE
     46             value: mysql
     47           - name: KONG_MYSQL_DATABASE
     48             value: kong
     49           - name: KONG_MYSQL_PASSWORD
     50             value: kong
     51           - name: KONG_MYSQL_HOST
     52             value: mysql.default.svc
     53           - name: KONG_LOG_LEVEL
     54             value: notice
     55           - name: KONG_ADMIN_ACCESS_LOG
     56             value: /dev/stdout
     57           - name: KONG_PROXY_ERROR_LOG
     58             value: /dev/stderr
     59           - name: KONG_ADMIN_ERROR_LOG
     60             value: /dev/stderr
     61           - name: KONG_ADMIN_LISTEN
     62             value: 'off'
     63           - name: KONG_PROXY_LISTEN
     64             value: 0.0.0.0:8000
     65         ports:
     66         - name: data-http
     67           containerPort: 8000
     68           hostPort: 8000
     69         readinessProbe:
     70           tcpSocket:
     71             port: 8000
     72           initialDelaySeconds: 5
     73           periodSeconds: 10
     74         livenessProbe:
     75           tcpSocket:
     76             port: 8000
     77           initialDelaySeconds: 15
     78           periodSeconds: 20
     79 
     80       - name: piped
     81         image: piped-alpine:0.1.0-59
     82         command: ["/usr/local/bin/piped"]
     83         imagePullPolicy: IfNotPresent
     84         env:
     85           - name: PIPED_CONFIG
     86             value: |+
     87               [common]
     88               log_level = info
     89 
     90               ;
     91               ; Flomesh probe
     92               ;
     93 
     94               [pipeline.probe]
     95               listen = 0.0.0.0:9999
     96 
     97               [module.1]
     98               name = flomesh-probe
     99 
    100               [module.2]
    101               name = dummy
    102 
    103 
    104               ;
    105               ; Prometheus metrics
    106               ;
    107 
    108               [pipeline.prometheus]
    109               listen = 0.0.0.0:10000
    110 
    111               [module.1]
    112               name = http-request-decoder
    113 
    114               [module.2]
    115               name = flomesh-prometheus
    116 
    117               [module.3]
    118               name = http-response-encoder
    119 
    120         ports:
    121         - name: probe-port
    122           containerPort: 9999
    123         - name: prometheus-port
    124           containerPort: 10000
    134           hostPort: 10000

在这里,主要的几个配置点是:

  • 第2行,类型为DaemonSet
  • 第68行和134行,定义了hostPort。这样会在容器宿主机的端口提供服务。实际情况是k8s添加了iptables的规则,把宿主机端口映射到了pod端口

这里和LoadBalancer方案的差异是,LB会首先用iptables把externalIP转发到clusertIP,然后clusterIP在转换到pod IP。可以看到,LB的方案会多一次iptables转发;另外clusterIP->podIP的转发可能是跨宿主网络的,而hostPort模式是本地转发。所以hostPort模式比LB模式有更好的性能

在实际中,为了分析性能,我们尽可能的避免引入iptables的规则。考虑到每个iptables的规则,如果乘以service的数量,会带来显著的性能差异

执行kubectl apply -f kong.yaml之后,很快就部署好了服务网关。这里我们就不详细记录了。

验证部署

首先我们确认k8s集群的状态(这里我们简化部署,采用两个node):

[root@master yaml2]# kubectl get nodes -o wide
NAME     STATUS   ROLES    AGE     VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
node2    Ready    dubbo    6d1h    v1.17.2+k3s1   192.168.122.22   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1
node1    Ready    dubbo    6d2h    v1.17.2+k3s1   192.168.122.21   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1
master   Ready    master   6d17h   v1.17.2+k3s1   192.168.122.20   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1

接下来确认pod的状态:

[root@master yaml2]# kubectl get pods -o wide 
NAME                                   READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
mysql-6f9fbdbd8b-wjp6h                 1/1     Running   13         6d16h   10.42.0.155   master   <none>           <none>
aiur-59df64664c-jhr9z                  2/2     Running   6          43h     10.42.0.157   master   <none>           <none>
fort-69fd64b955-p6j7g                  2/2     Running   4          39h     10.42.0.158   master   <none>           <none>
kong-8swnv                             2/2     Running   7          43h     10.42.0.153   master   <none>           <none>
kong-mjmpk                             2/2     Running   6          43h     10.42.1.103   node1    <none>           <none>
kong-7h4h6                             2/2     Running   6          43h     10.42.2.98    node2    <none>           <none>

接下来我们确认服务网关正常工作,我们先通过podIP:8000来访问,然后通过hostIP:8000来访问:

[root@master ~]# curl -i 10.42.2.98:8000
HTTP/1.1 404 Not Found
Date: Tue, 25 Feb 2020 10:14:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 23

{"message":"Not Found"}
[root@master ~]# curl -i 192.168.122.22:8000
HTTP/1.1 404 Not Found
Date: Tue, 25 Feb 2020 10:16:22 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 23

{"message":"Not Found"}

部署bookinfo应用

如果已经“下载和导入镜像”,部署应用非常简单:

kubectl apply -f bookinfo-ratings.yaml
kubectl apply -f bookinfo-reviews.yaml

这里就可以正常访问reviews和ratings服务了。

可以配置访问控制,具体可以参考这里

访问ratings服务:

[root@master ~] curl -i -H "apikey: bookinfo-reviews-v1" http://192.168.122.20:8000/ratings/a071c269-369c-4f79-be03-6a41f27d6b5f
HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Application-Context: bookinfo-ratings:8101
Date: Tue, 25 Feb 2020 10:43:59 GMT

[{"id":"7a846fe8-fec5-4437-a5ba-1af0210a47ce","reviewerId":"ea743176-e7e8-4660-b6db-5ce08457e76f","productId":"a071c269-369c-4f79-be03-6a41f27d6b5f","rating":1}]

访问reviews服务:

[root@master ~] curl -i -H "apikey: bookinfo-reviews-v1" http://192.168.122.20:8000/reviews/a071c269-369c-4f79-be03-6a41f27d6b5f
HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Application-Context: bookinfo-reviews:8101
Date: Tue, 25 Feb 2020 10:47:26 GMT

[{"id":"06f81906-7f1c-4707-b99c-3a9c20cfbeb2","reviewerId":"ea743176-e7e8-4660-b6db-5ce08457e76f","productId":"a071c269-369c-4f79-be03-6a41f27d6b5f","review":"Could not finish the book. Too boring.","rating":1}]

yaml详解

这里我们对reviews服务的yaml做一个解释,一方面帮助理解部署,一方面帮助理解Flomesh的工作原理和细节。让我们看下reviews服务的yaml(为了便于介绍,保留了行号):

      1 ##################################################################################################
      2 # Reviews service
      3 ##################################################################################################
      4 apiVersion: v1
      5 kind: Service
      6 metadata:
      7   name: bookinfo-reviews
      8   labels:
      9     app: reviews
     10     service: reviews
     11 spec:
     12   type: ClusterIP
     13   clusterIP: None
     14   selector:
     15     app: reviews
     16 ---
     17 apiVersion: apps/v1
     18 kind: Deployment
     19 metadata:
     20   name: bookinfo-reviews-v1
     21   labels:
     22     app: reviews
     23     version: v1
     24 spec:
     25   replicas: 2
     26   selector:
     27     matchLabels:
     28       app: reviews
     29       version: v1
     30   template:
     31     metadata:
     32       labels:
     33         app: reviews
     34         version: v1
     35     spec:
     36       initContainers:
     37       - image: docker.io/library/piped-alpine:0.1.0-59
     38         imagePullPolicy: IfNotPresent
     39         name: init
     40         env:
     41         - name: IPTABLES_RULE
     42           value: "-t nat -A OUTPUT -p tcp ! -d 127.0.0.1  --dport 9080 -j REDIRECT --to-ports 9090"
     43         command: ["/sbin/iptables"]
     44         args: ["-t", "nat", "-A", "OUTPUT", "-p", "tcp", "!", "-d", "127.0.0.1", "--dport", "9080", "-j", "REDIRECT", "--to-ports", "9090"]
     45         securityContext:
     46           capabilities:
     47             add:
     48             - NET_ADMIN
     49 
     50       containers:
     51       - name: reviews
     52         image: docker.io/library/bookinfo-reviews:latest
     53         imagePullPolicy: IfNotPresent
     54         ports:
     55         - containerPort: 8101
     56           name: java
     57         env:
     58           - name: RATINGS_URL
     59             value: http://bookinfo-ratings.default.svc:9080/
     60       - name: piped
     61         image: docker.io/library/piped-alpine:0.1.0-59
     62         command: ["/usr/local/bin/piped"]
     63         imagePullPolicy: IfNotPresent
     64         ports:
     65         - name: inbound
     66           containerPort: 8080
     67         env:
     68           - name: K8S_NODE_NAME
     69             valueFrom:
     70               fieldRef:
     71                 fieldPath: spec.nodeName
     72           - name: K8S_POD_NAME
     73             valueFrom:
     74               fieldRef:
     75                 fieldPath: metadata.name
     76           - name: K8S_POD_NAMESPACE
     77             valueFrom:
     78               fieldRef:
     79                 fieldPath: metadata.namespace
     80           - name: K8S_HOST_IP
     81             valueFrom:
     82               fieldRef:
     83                 fieldPath: status.hostIP
     84           - name: K8S_POD_IP
     85             valueFrom:
     86               fieldRef:
     87                 fieldPath: status.podIP
     88 
     89           - name: PIPED_CONFIG
     90             value: |+
     91               ;
     92               ;inbound
     93               ;
     94               [pipeline.inbound]
     95               listen = 0.0.0.0:8080
     96 
     97               [module.http-decode]
     98               name = http-request-decoder
     99 
    100               [module.throttle]
    101               name = tap
    102               limit = 500
    103 
    104               [module.http-encode]
    105               name = http-request-encoder
    106 
    107               [module.inbound-proxy]
    108               name = proxy
    109               upstream = 127.0.0.1:8101
    110 
    111               ;
    112               ;outbound
    113               ;
    114               [pipeline.outbound]
    115               listen = 127.0.0.1:9090
    116 
    117               [module.http-decode]
    118               name = http-request-decoder
    119 
    120               [module.http-add-header]
    121               name = http-request-headers
    122               K8S_HOST_IP = $(K8S_HOST_IP)
    123               K8S_NODE_NAME = $(K8S_NODE_NAME)
    124               K8S_POD_NAME = $(K8S_POD_NAME)
    125               K8S_POD_NAMESPACE = $(K8S_POD_NAMESPACE)
    126               K8S_POD_IP = $(K8S_POD_IP)
    127               apikey = bookinfo-reviews-v1
    128 
    129               [module.http-request-encode]
    130               name = http-request-encoder
    131 
    132               [module.proxy]
    133               name = proxy
    134               upstream = $(K8S_HOST_IP):8000

Service定义

这里看#12和#13行,定义了服务的类型是clusterIP,同时定义了该Service不需要ClusterIP。在Flomesh里,我们推荐使用内部域名做通讯,而不是ClusterIP。同时,是否使用ClusterIP在于:如果clusterIP=None,那么k8s不会添加iptables规则;如果没有这个设置,会创建一条到多条的iptables规则,在每个node上。在k8s里,ClusterIP和内部DNS,本质都是完成服务注册和服务发现。如果确认使用内部域名,那么其实就没有必要使用ClusterIP了。

但是需要注意,并非所有的时候都可以设置clusterIP=None,因为有些k8s的功能是依赖clusterIP的。比如LoadBalancer就需要经过ClusterIP做一次转发和负载。

outbound流量的拦截

配置中的#36~#48行,用一个initContainer,注入了一条iptables规则:

iptables -t nat -A OUTPUT -p tcp ! -d 127.0.0.1  --dport 9080 -j REDIRECT --to-ports 9090

这个规则的意思是:对于目标IP地址不是127.0.0.1,且目标端口为9080的请求,转发到9090端口(127.0.0.1:9090)端口。这里,9080为review服务向外调用其他服务所用的端口;9090端口是outbound proxy监听的端口。

实际上为了完成outbound的拦截,也可以不用iptables,但是需要改造服务程序或者配置本身

对比istio,istio的iptables规则注入默认采用mutating webhook admission controller来注入,也就是拦截kubernetes的api,通过hook修改请求来注入功能,类似spring的AOP。目前Flomesh采用手动办法做流量拦截

piped作为sidecar

配置中#60行之后,定义了piped容器,piped作为和reviews服务(51-59行)在一个pod内以sidecar方式部署。piped是Flomesh团队开发的sidecar proxy程序,设计目标是小尺寸、高性能、易用易调试。这个例子中的piped镜像包含了一些用于调试网络的小工具,比如curl、nc、telnet、netstat等,尺寸为13.1M;如果不包含这些调试工具,尺寸在10M之内。

[root@node1 ~]# crictl images | grep piped
docker.io/library/piped-alpine       0.1.0-59            5fdc10c27d0ef       13.1MB

piped是高性能的,在最简单的http代理情况下,性能略优于nginx。主要原因是piped针对proxy开发,并不是完整的http服务软件。

这里piped的配置采用环境变量传入。当piped启动的时候,如果没有声明配置文件,且存在名为PIPED_CONFIG的环境变量,piped会从该环境变量中加载配置。在例子中,#89行到#134行是该环境变量的内容。配置分为两个部分,#91~#110行定义了一个pipeline,配置了inbound处理;#111~#134行定义了第二个pipeline,配置了outbound处理。

piped是基于流计算的,所以运行时占用内存也非常少。每一个流的定义被称为一个pipeline。piped是模块化的,模块主要分为三类:一类是7层协议的处理,称为frame与deframe;一类是编码与解码的处理,就是code与decode;一类是功能处理,比如替换内容。举个例子,如果在http上传输json,那么首先需要解析http的frame,然后解码http协议,然后解析json(如果需要的话)。同样的情况,如果是dubbo处理,那么就是先分拆dubbo的frame,然后解析dubbo协议,然后解码hession2编码(假设用的是hession2编码)。需要注意的是,整个过程都是基于流计算的,所以当解码json的时候,并没有在内存中构建DOM,处理速度和内存占用都有很大优势。

piped是模块化的,目前已经开发了很多模块,并且有一些正在开发的模块。目前piped并不是开源软件。

inbound处理

配置中的#91~#110行是pod中inbound proxy的配置。

  • #95行定义了监听在0.0.0.0的8080端口
  • #97和#98行定义了解析http协议
  • #100~#102行,定义了一个Throttle模块:每秒piped只允许通过500个http请求,超过的部分会队列化处理。这个功能我们成为“流量稳压器”或者“流量阀门”
  • #104~#105行定义了把内容做http编码,也就是在发给应用之前,做http编码
  • #107~#109行定义了“代理”模块,作用是把请求代理到127.0.0.1:8101,在例子中,就是Java Spring实现的bookinfo-reviews服务

需要注意的是,Flomesh的设计不同于istio:istio是屏蔽应用原生端口,通过proxy拦截请求,然后转发给应用;Flomesh是同时保持应用的端口开放和proxy端口开放。 这样做主要有两个好处:

  1. 当同时开放原生端口和代理端口的时候,更容易调试
  2. 减少iptables规则的数量。istio的inbound流量拦截需要一些iptables规则才能实现,当service数量很多的时候,引入的iptables规则也很多

Throttle模块非常有用。本文最后的附录里我们会用一个例子来简单的对比流量通过该模块和不通过的差异。通过每个Pod设置Throttle,可以有效的保障容器内应用不被突发大的流量冲击导致失败。Throttle是服务质量(QoS)和服务等级(SLA)实现的基础

在这个例子中,我们只做了一个Throttle的处理。根据需要,可以配置piped采用不同的模块实现其他的功能,比如字段级的解密等

outbound处理

配置的#111行到#134行是outbound proxy的配置。

  • #114~#115行配置了监听在127.0.0.1:9090端口。记得通过initContainer注入的iptables规则吧,流量被拦截到这个地址
  • #120~#127行是“添加HTTP Header模块”,作用是在请求中添加Header,比如#122行添加了一个header,名字是K8S_HOST_IP,值来自环境变量K8S_HOST_IP。这个环境变量是#80~#83行通过downward API(参考这里)注入到容器环境中的
  • #127行添加了名为apikey的header,这个header在Flomesh中是可以配置的,可以用来做基本的服务之间访问控制和身份识别。参考这里

在这个例子中,我们只配置了添加HTTP Header,并且除了apikey,其他的header我们并没有实际使用。根据需要,可以配置piped实现其他的功能,比如字段级的加密、格式转换(json->xml互转)等

众所周知,环境变量的使用,在容器和k8s中非常常见。通过downward API和“添加HTTP Header模块”的搭配使用,我们能把host/pod/service/container等多个级别的运行时信息,以http header的方式传递出去,用于后续做处理的判断依据

参考BeyondCorp的实现,通过在运行时在proxy中添加header,可以用于实现很多功能。这个添加header的过程,我们称为“信息增强(enrich)”,这些增强出来的信息可以用于后续的逻辑判断,比如在服务网关中,用lua来判断header,然后做不同的操作

验证

部署好以后,我们可以访问应用,在Flomesh控制台,可以看到Mesh的各种相关功能实现:流量、负载、访问控制、可视化、服务拓扑,也可以配置其他功能,比如蓝绿发布等,目前已有的功能可以参考Flomesh文档:/doc/content.html


附录:验证和对比Throttle

之前我们介绍过,对于inbound请求,Flomesh同时保留了proxy端口和应用原生端口,这里我们在Flomesh控制台配置两个API,ratings和ratings-raw,其中ratings服务访问的是代理端口,也就是有Throttle功能;ratings-raw访问原生的Java Spring REST端口(在例子中由Tomcat提供)。

经过proxy带有Throttle功能的配置

不经过proxy配置

我们通过ab -c 10 -n 50000来发送50000个请求,来对比是否使用Throttle的差异:

在如下截图中,我们采用了一个pod运行ratings服务,也就是replicas=1。默认的yaml部署是replicas=2

不使用Throttle

参考如上的截图中的配置,如果访问带有Host: raw.default.svc的话,请求会被代理和负载到8101端口,也就是原生Java服务,不使用Throttle

[root@master ~] ab -c 10 -n 50000 -H "Host: raw.default.svc" -H "apikey: bookinfo-reviews-v1" http://192.168.122.20:8000/ratings/a071c269-369c-4f79-be03-6a41f27d6b5f
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.122.20 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:        
Server Hostname:        192.168.122.20
Server Port:            8000

Document Path:          /ratings/a071c269-369c-4f79-be03-6a41f27d6b5f
Document Length:        161 bytes

Concurrency Level:      10
Time taken for tests:   44.196 seconds
Complete requests:      50000
Failed requests:        0
Total transferred:      16300000 bytes
HTML transferred:       8050000 bytes
Requests per second:    1131.32 [#/sec] (mean)
Time per request:       8.839 [ms] (mean)
Time per request:       0.884 [ms] (mean, across all concurrent requests)
Transfer rate:          360.17 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       8
Processing:     1    9  10.1      7     465
Waiting:        1    8   9.3      6     458
Total:          1    9  10.1      7     466

Percentage of the requests served within a certain time (ms)
  50%      7
  66%      9
  75%     10
  80%     11
  90%     14
  95%     19
  98%     33
  99%     45
 100%    466 (longest request)

访问原生端口

使用Throttle

访问代理端口

[root@master ~] ab -c 10 -n 50000 -H "apikey: bookinfo-reviews-v1" http://192.168.122.20:8000/ratings/a071c269-369c-4f79-be03-6a41f27d6b5f
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.122.20 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:        
Server Hostname:        192.168.122.20
Server Port:            8000

Document Path:          /ratings/a071c269-369c-4f79-be03-6a41f27d6b5f
Document Length:        161 bytes

Concurrency Level:      10
Time taken for tests:   99.975 seconds
Complete requests:      50000
Failed requests:        0
Total transferred:      16300000 bytes
HTML transferred:       8050000 bytes
Requests per second:    500.13 [#/sec] (mean)
Time per request:       19.995 [ms] (mean)
Time per request:       1.999 [ms] (mean, across all concurrent requests)
Transfer rate:          159.22 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       8
Processing:     1   20  41.9      6     601
Waiting:        1    9  38.6      2     601
Total:          1   20  41.9      6     601

Percentage of the requests served within a certain time (ms)
  50%      6
  66%     10
  75%     41
  80%     41
  90%     42
  95%     44
  98%     73
  99%    231
 100%    601 (longest request)

结论

对比如上两个验证性测试,可以看到,通过使用Throttle功能,带来了平稳的访问性能。

像很多工程领域问题一样,我们在期望“系统整体平稳运行”,人们追求“可靠性”和“确定性”,那么我们就需要在系统的每个环节去寻找保障“确定性”的方法。就像几乎所有我们今天使用的电子设备一样,每个独立的电路,都有“稳压器”;对于数字电路而言,都有波形整形器;微服务作为一个由很多小的单元组成的整体,它的整体可靠性和稳定性,来自于每个小的单元的稳定、可靠、可控,piped的Throttle功能就是实现这个的基础,就是微服务世界里的“稳压器”和“波形整形器”。