概述http chunkedwatch api

概述

Kubernetes 中主要通过 List-Watch 机制实现组件间的异步消息通信,List-Watch 机制的实现原理值得深入分析下 。

Kubernetes client-go 源码分析 - ListWatcher 中我们讲到 client-goListWatcher 是如何实现 List()Watch() 两个方法的,在那篇文章里我们一路跟到 RESTClient 调用 apiserver 后没有就继续往下挖掘了,因为下面的技术相对独立,要聊清楚也需要不小的篇幅,于是我们单独在“原理篇”里分析 List-Watch 的实现。

今天我们先从 http 协议层面来分析 watch 的实现机制,抓包看下调用 watch 接口时数据包流向是怎样的。

http chunked

Kubernetes 里的 watch 长链接是通过 http 协议 chunked 机制实现的,在响应头里加一个 Transfer-Encoding: chunked 就可以实现分段响应。我们用 golang 来模拟一下这个过程,从而先理解 chunked 是什么。

写个 demo 程序,server 端代码如下:

 1func Server() {
2    http.HandleFunc("/name", func(w http.ResponseWriter, r *http.Request) {
3        flusher := w.(http.Flusher)
4        for i := 0; i < 2; i++ {
5            fmt.Fprintf(w, "Daniel Hu\n")
6            flusher.Flush()
7            <-time.Tick(1 * time.Second)
8        }
9    })
10    log.Fatal(http.ListenAndServe(":8080", nil))
11}

这里的逻辑是当客户端请求 localhost:8080/name 的时候,服务器端响应两段:"Daniel Hu\n",然后直接运行,再随便用什么工具访问一下,比如 curl localhost:8080/name,抓个包可以看到如下响应体:

chunked 类型的 response 由一个个 chunk 组成,每个 chunk 都是格式都是 Chunk size + Chunk data + Chunk boundary,也就是块大小+数据+边界标识。chunk 的结尾是一个大小为0的 chunk,也就是"0\r\n"。串在一起整体格式类似这样:

  • [Chunk size][Chunk data][Chunk boundary][Chunk size][Chunk data][Chunk boundary][Chunk size=0][Chunk boundary]

在上图的例子中,服务器端响应的内容是两个相同的字符串 "Daniel Hu\n",客户端拿到的也就是是 "10Daniel Hu\n\r\n10Daniel Hu\n\r\n0\r\n"

这种类型的数据怎么接收呢?可以这样玩:

 1func Client() {
2    resp, err := http.Get("http://127.0.0.1:8080/name")
3    if err != nil {
4        log.Fatal(err)
5    }
6    defer resp.Body.Close()
7
8    fmt.Println(resp.TransferEncoding)
9
10    reader := bufio.NewReader(resp.Body)
11    for {
12        line, err := reader.ReadString('\n')
13        if len(line) > 0 {
14            fmt.Print(line)
15        }
16        if err == io.EOF {
17            break
18        }
19        if err != nil {
20            log.Fatal(err)
21        }
22    }
23}

输出内容如下(两个字符串中间会间隔1s):

1[chunked]
2Daniel Hu
3Daniel Hu

http 协议的 chunked 类型响应数据方式大概就是这个玩法,接下来我们看下调用 Kubernetes api 的时候,能不能找到里面的 chunked 痕迹。

watch api

一般现在搭建集群都是 https 暴露 api 了,而且是双向 TLS,所以我们需要先代理一道,方便调用和抓包。

本地通过 kubectl 来代理 apiserver 暴露外部访问:

  • kubectl proxy
1# kubectl proxy
2Starting to serve on 127.0.0.1:8001

然后开始 watch 一个资源,比如我这里选择 coredns 的 configmap:

  • curl localhost:8001/api/v1/watch/namespaces/kube-system/configmaps/coredns
1# curl localhost:8001/api/v1/watch/namespaces/kube-system/configmaps/coredns
2{"type":"ADDED","object":{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"coredns","namespace":"kube-system","uid":"f2eb4080-ce86-436a-9cfb-5bfdd3f59433","resourceVersion":"747388","creationTimestamp":"2021-09-06T02:49:56Z","managedFields":[{"manager":"kubeadm","operation":"Update","apiVersion":"v1","time":"2021-09-06T02:49:56Z","fieldsType":"FieldsV1","fieldsV1":{"f:data":{}}},{"manager":"kubectl-edit","operation":"Update","apiVersion":"v1","time":"2021-10-09T10:16:13Z","fieldsType":"FieldsV1","fieldsV1":{"f:data":{"f:Corefile":{}}}}]},"data":{"Corefile":".:53 {\n    errors\n    health {\n       lameduck 5s\n    }\n    ready\n    kubernetes cluster.local in-addr.arpa ip6.arpa {\n       pods insecure\n       fallthrough in-addr.arpa ip6.arpa\n       ttl 30\n    }\n    prometheus :9153\n    forward . /etc/resolv.conf {\n       max_concurrent 1000\n    }\n    cache 30\n    loop\n    reload\n    loadbalance\n}\n"}}}

这时候可以马上拿到一个响应,然后我们通过 kubectl 命令去编辑一下这个 configmap,可以看到 watch 端继续收到一条消息:

1{"type":"MODIFIED","object":{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"coredns","namespace":"kube-system","uid":"f2eb4080-ce86-436a-9cfb-5bfdd3f59433","resourceVersion":"747834","creationTimestamp":"2021-09-06T02:49:56Z","managedFields":[{"manager":"kubeadm","operation":"Update","apiVersion":"v1","time":"2021-09-06T02:49:56Z","fieldsType":"FieldsV1","fieldsV1":{"f:data":{}}},{"manager":"kubectl-edit","operation":"Update","apiVersion":"v1","time":"2021-10-09T10:16:13Z","fieldsType":"FieldsV1","fieldsV1":{"f:data":{"f:Corefile":{}}}}]},"data":{"Corefile":".:53 {\n    errors\n    health {\n       lameduck 5s\n    }\n    ready\n    kubernetes cluster.local in-addr.arpa ip6.arpa {\n       pods insecure\n       fallthrough in-addr.arpa ip6.arpa\n       ttl 30\n    }\n    prometheus :9153\n    forward . /etc/resolv.conf {\n       max_concurrent 1000\n    }\n    cache 31\n    loop\n    reload\n    loadbalance\n}\n"}}}

原来 apiserver 是这样将资源变更通知到 watcher 的。

这时候如果我们去抓包,依旧可以看到这两个响应信息的具体数据包格式,第一个响应体如下(截取了中间关键信息):

 1// ……
20030   fe d7 b6 90 af fc 85 ac 48 54 54 50 2f 31 2e 31   ........HTTP/1.1
30040   20 32 30 30 20 4f 4b 0d 0a 41 75 64 69 74 2d 49    200 OK..Audit-I
4// ……
50160   2d 62 66 38 38 66 37 66 39 66 33 66 61 0d 0a 54   -bf88f7f9f3fa..T
60170   72 61 6e 73 66 65 72 2d 45 6e 63 6f 64 69 6e 67   ransfer-Encoding
70180   3a 20 63 68 75 6e 6b 65 64 0d 0a 0d 0a 33 61 38   : chunked....3a8
80190   0d 0a 7b 22 74 79 70 65 22 3a 22 41 44 44 45 44   ..{"type":"ADDED
901a0   22 2c 22 6f 62 6a 65 63 74 22 3a 7b 22 6b 69 6e   ","object":{"kin
1001b0   64 22 3a 22 43 6f 6e 66 69 67 4d 61 70 22 2c 22   d":"ConfigMap","
11// ……
12

可以看到这里的 http 头有一个 Transfer-Encoding: chunked,下面的内容是 {"type":"ADDED…

继续看第二个包,第二个简单很多,少了 http 头信息,只是简单的第二个 chunk,长这样:

1// ……
20030   fe d7 fb 0b af fc c0 4a 33 61 62 0d 0a 7b 22 74   .......J3ab..{"t
30040   79 70 65 22 3a 22 4d 4f 44 49 46 49 45 44 22 2c   ype":"MODIFIED",
40050   22 6f 62 6a 65 63 74 22 3a 7b 22 6b 69 6e 64 22   "object":{"kind"
50060   3a 22 43 6f 6e 66 69 67 4d 61 70 22 2c 22 61 70   :"ConfigMap","ap
6// ……
7

这里可以看到 0d 0a,出于程序员的职业敏感,得想到这个就是 \r\n,至于前面的 3ab,猜到了吗?转十进制就是 939,对应这个 chunk 的长度,这里和前面我们自己写的 http server 请求-响应格式就一致了。

(转载请保留本文原始链接 https://www.danielhu.cn)

Kubernetes List-Watch 机制原理与实现 - chunked的更多相关文章

  1. Atitit事件代理机制原理 基于css class的事件代理

    Atitit事件代理机制原理 基于css class的事件代理 1.1. 在javasript中delegate这个词经常出现,看字面的意思,代理.委托1 1.2. 事件代理1 1.3. 代理标准化规 ...

  2. Atitit.软件与编程语言中的锁机制原理attilax总结

    Atitit.软件与编程语言中的锁机制原理attilax总结 1. 用途 (Db,业务数据加锁,并发操作加锁.1 2. 锁得类型 排它锁 "互斥锁 共享锁 乐观锁与悲观锁1 2.1. 自旋锁 ...

  3. Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer

    Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer 1. Java NIO(New Input/Output)1 1.1. 变更通知(因为每个事件都需要一个监听者 ...

  4. OAuth的机制原理讲解及开发流程

    本想前段时间就把自己通过QQ OAuth1.0.OAuth2.0协议进行验证而实现QQ登录的心得及Demo实例分享给大家,可一直很忙,今天抽点时间说下OAuth1.0协议原理,及讲解下QQ对于Oaut ...

  5. ASP.NET运行机制原理

    ASP.NET运行机制原理 一.浏览器和服务器的交互原理 (一).浏览器和服务器交互的简单描述: 1.通俗描述:我们平时通过浏览器来访问网站,其实就相当于你通过浏览器去另一台电脑上访问文件一样,只不过 ...

  6. ASP.NET运行机制原理 ---浏览器与IIS的交互过程 自己学习 网上查了下别人写的总结的很好 就转过来了 和自己写的还好里嘻嘻

    一.浏览器和服务器的交互原理 (一).浏览器和服务器交互的简单描述: 1.通俗描述:我们平时通过浏览器来访问网站,其实就相当于你通过浏览器去访问一台电脑上访问文件一样,只不过浏览器的访问请求是由被访问 ...

  7. UITableViewCell的重用机制原理

    UITableViewCell的重用机制原理 来自http://blog.csdn.net/omegayy/article/details/7356823 ====================== ...

  8. 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】

    原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...

  9. 剖析Qt的事件机制原理

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消息循环和WinMai ...

随机推荐

  1. vue 输入框禁止输入空格 ,只能输入数字,禁止输入数字

    正则表达式: @input="form.userName = form.userName.replace(/\s+/g,'')"    ( 禁止输入空格)    @input=&q ...

  2. Django——数据库连接配置

    配置settings.py : DATABASES = { 'default': { #default表示默认,也可以指定app 'ENGINE': 'django.db.backends.mysql ...

  3. 浅析Is-a,Has-a与like-a

    在面向对象的设计领域里,有很多设计思路,主要有三种:is-a.has-a.like-a. 这三种在java的类.接口.抽象类中很多体现,下面简述一下其定义. 1.Is-a(继承关系) is-a,顾名思 ...

  4. 《通过刷leetcode学习Go语言》之(1):序言

    Author       : Email         : vip_13031075266@163.com Date          : 2021.03.07 Version     : 北京 C ...

  5. 快速模式第二包: quick_inI1_ouR1()

    文章目录 1. 序言 2. quick_inI1_outR1()流程图 3. 快速模式消息②数据包格式 4. 源码分析 4.1 quick_inI1_outR1() 4.2 quick_inI1_ou ...

  6. [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块

    [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 目录 [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 0x00 摘要 0x01 前言 0x02 ...

  7. JS边角料: NodeJS+AutoJS+WebSocket+TamperMonkey实现局域网多端文字互传

    ---阅读时间约 7 分钟,复现时间约 15 分钟--- 由于之前一直在用的扩展 QPush 停止服务了,苦于一人凑齐了 Window, Android, Mac, ios 四种系统的设备,Apple ...

  8. ysoserial CommonsColletions6分析

    CC6的话是一条比较通用的链,在JAVA7和8版本都可以使用,而触发点也是通过LazyMap的get方法. TiedMapEntry#hashCode 在CC5中,通过的是TiedMapEntry的t ...

  9. Ansible快速实战指南----多机自动化执行命令、部署神器

                                      1.需求: 需要在多台主机上,发送文件.执行命令,进行快速部署 2.ansible 远程复制文件 例子:在当前节点(20.88.14 ...

  10. HDFS基本命令

    1.创建目录 hadoop dfs -mkdir /data hadoop dfs -mkdir -p /data/data1   创建多级目录 2.查看文件 hadoop dfs -ls / 3.上 ...