|
| 1 | +--- |
| 2 | +title: "MoE 系列(一)|如何使用 Golang 扩展 Envoy" |
| 3 | +authorlink: "https://github.com/sofastack" |
| 4 | +description: "MoE 系列(一)|如何使用 Golang 扩展 Envoy" |
| 5 | +categories: "SOFAStack" |
| 6 | +tags: ["SOFAStack"] |
| 7 | +date: 2023-03-28T15:00:00+08:00 |
| 8 | +cover: "https://mdn.alipayobjects.com/huamei_soxoym/afts/img/A*fPOiTJsdpxMAAAAAAAAAAAAADrGAAQ/original" |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +*本文作为 MoE 系列第一篇,主要介绍用 Golang 扩展 Envoy 的极速开发体验。* |
| 13 | + |
| 14 | +**一、背景** |
| 15 | + |
| 16 | +MoE*(MOSN on Envoy)*是 MOSN 团队提出的技术架构,经过近两年的发展,在蚂蚁内部已经得到了很好的验证;并且去年我们也将底层的 Envoy Go 七层扩展贡献了 Envoy 官方,MOSN 也初步支持了使用 Envoy 作为网络底座的能力。 |
| 17 | + |
| 18 | +借此准备写一系列的文章,逐一介绍这里面的技术。本文作为开篇,将重点介绍 MoE 中的基础技术,Envoy Go 扩展。 |
| 19 | + |
| 20 | +**二、FAQ** |
| 21 | + |
| 22 | +开始前,先给大家解答下几个基本的问题: |
| 23 | + |
| 24 | +> **1、MoE 与 Envoy Go 扩展有什么区别?** |
| 25 | +
|
| 26 | +A:MoE 是技术架构,Envoy Go 扩展是连接 MOSN 和 Envoy 的基础技术。 |
| 27 | + |
| 28 | +> **2、Envoy Go 扩展,与用 Go 来编译 Wasm 有什么区别?** |
| 29 | +
|
| 30 | +A:Envoy Go 支持 Go 语言的所有特性,包括 Goroutine;Go Wasm 则只能使用少量的 Go 语言特性,尤其是没有 Goroutine 的支持。 |
| 31 | + |
| 32 | +> **3、Go 是静态链接到 Envoy 么?** |
| 33 | +
|
| 34 | +A:不是的,Go 扩展编译成为 so,Envoy 动态加载 so,不需要重新编译 Envoy |
| 35 | + |
| 36 | +> **4、Envoy Go 支持流式处理么?** |
| 37 | +
|
| 38 | +A:支持的。 |
| 39 | + |
| 40 | +由于 Go 扩展提供的是底层的 API,非常的灵活,使用上相对会稍微复杂一些;如果只想简单的使用,可以使用 MOSN 的 filter,后面我们也会介绍。 |
| 41 | + |
| 42 | +**三、需求** |
| 43 | + |
| 44 | +我们先实现一个小需求,来实际体会一下: |
| 45 | + |
| 46 | +对请求需要进行验签,大致是从 URI 上的某些参数,以及私钥计算一个 token,然后和 header 中的 token 进行对比,对不上就返回 403。 |
| 47 | + |
| 48 | +很简单的需求,仅仅作为示例,主要是体验一下过程。 |
| 49 | + |
| 50 | +**四、代码实现** |
| 51 | + |
| 52 | +完整的代码可以看 envoy-go-filter-example[1] 这个仓库,这里摘录最核心的两个函数: |
| 53 | + |
| 54 | +```bash |
| 55 | +const secretKey = "secret" |
| 56 | + |
| 57 | +func verify(header api.RequestHeaderMap) (bool, string) { |
| 58 | + token, ok := header.Get("token") |
| 59 | + if ok { |
| 60 | + return false, "missing token" |
| 61 | + } |
| 62 | + |
| 63 | + path, _ := header.Get(":path") |
| 64 | + hash := md5.Sum([]byte(path + secretKey)) |
| 65 | + if hex.EncodeToString(hash[:]) != token { |
| 66 | + return false, "invalid token" |
| 67 | + } |
| 68 | + return true, "" |
| 69 | +} |
| 70 | + |
| 71 | +func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api.StatusType { |
| 72 | + if ok, msg := verify(header); !ok { |
| 73 | + f.callbacks.SendLocalReply(403, msg, map[string]string{}, 0, "bad-request") |
| 74 | + return api.LocalReply |
| 75 | + } |
| 76 | + return api.Continue |
| 77 | +} |
| 78 | +``` |
| 79 | +
|
| 80 | +`DecodeHeaders` 是扩展 `filter` 必须实现的方法,我们就是在这个阶段对请求 `header` 进行校验。 |
| 81 | +
|
| 82 | +`verify` 是校验函数,这里的 `RequestHeaderMap` 是 Go 扩展提供的 `interface`,我们可以通过它来读写 header,其他都是常见的 Go 代码写法。 |
| 83 | +
|
| 84 | +**五、编译** |
| 85 | +
|
| 86 | +编译很简单,与常见的 Go 编译一样,这里我们使用 Golang 官方的 docker 镜像来编译: |
| 87 | +
|
| 88 | +```bash |
| 89 | +docker run --rm -v `pwd`:/go/src/go-filter -w /go/src/go-filter \ |
| 90 | +-e GOPROXY=https://goproxy.cn \ |
| 91 | +golang:1.19 \ |
| 92 | +go build -v -o libgolang.so -buildmode=c-shared . |
| 93 | +``` |
| 94 | +
|
| 95 | +Go 编译还是很快的,只需要几秒钟,当前目录下,就会产生一个 libgolang.so 的文件。反观 Envoy 的编译速度,一次全量编译动辄几十分钟,或者上小时的,这幸福感提升了不止一个档次。🥰 |
| 96 | +
|
| 97 | +**六、运行** |
| 98 | +
|
| 99 | +我们可以使用 Envoy 官方提供的镜像来运行,如下示例: |
| 100 | +
|
| 101 | +```bash |
| 102 | +docker run --rm -v `pwd`/envoy.yaml:/etc/envoy/envoy.yaml \ |
| 103 | + -v `pwd`/libgolang.so:/etc/envoy/libgolang.so \ |
| 104 | + -p 10000:10000 \ |
| 105 | + envoyproxy/envoy:contrib-dev \ |
| 106 | + envoy -c /etc/envoy/envoy.yaml |
| 107 | +``` |
| 108 | +
|
| 109 | +只需要把上一步编译的 `libgolang.so` 和 `envoy.yaml` 挂载进去就可以了。 |
| 110 | +
|
| 111 | +值得一提的是,我们需要在 envoy.yaml 配置中启用 Go 扩展,具体是这段配置: |
| 112 | +
|
| 113 | +```bash |
| 114 | +http_filters: |
| 115 | + - name: envoy.filters.http.golang |
| 116 | + typed_config: |
| 117 | + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config |
| 118 | + library_id: example |
| 119 | + library_path: /etc/envoy/libgolang.so |
| 120 | + plugin_name: example-1 |
| 121 | +``` |
| 122 | +
|
| 123 | +跑起来之后,我们测试一下: |
| 124 | +
|
| 125 | +```bash |
| 126 | +$ curl 'http://localhost:10000/' |
| 127 | +missing token |
| 128 | +$ curl -s 'http://localhost:10000/' -H 'token: c64319d06364528120a9f96af62ea83d' -I |
| 129 | +HTTP/1.1 200 OK |
| 130 | +``` |
| 131 | +
|
| 132 | +符合期望,是不是很简单呢? |
| 133 | +
|
| 134 | +**七、后续** |
| 135 | +
|
| 136 | +什么?这个示例太简单? |
| 137 | +
|
| 138 | +是的,这里主要是体验下开发流程,下篇我们将再介绍更高级的玩法: |
| 139 | +
|
| 140 | +**Go 接受来自 Envoy 侧的配置、异步 Goroutine,以及与 Istio 配合的用法。** |
| 141 | +
|
| 142 | +**|相关链接|** |
| 143 | +
|
| 144 | +[1]envoy-go-filter-example 仓库: |
| 145 | +
|
| 146 | +[*https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-1*](https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-1) |
| 147 | +
|
| 148 | +**了解更多…** |
| 149 | +
|
| 150 | +**MOSN Star 一下✨:** |
| 151 | +[*https://github.com/mosn/mosn*](https://github.com/mosn/mosn) |
0 commit comments