在阅读了etcd server的启动流程后,我们对很多关键性函数的入口都有了初步印象。
那么,接下来我们一起看看对键值对的修改,在etcd server内部是怎么流转的。
PUT键值对的HTTP请求
用etcdctl
这个指令,我们可以快速地用命令etcdctl put key value
发送PUT键值对的请求。
但etcdctl
对请求做了封装,我们要了解原始的HTTP请求格式,才能方便地阅读相关代码。相关的途径有很多,比如抓包、读源码等,这里为了可阅读性,我给出一个curl
请求。
1 | curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key":"mykey","value":"myvalue"}' |
主要关注如下三点:
- Method -
POST
- URL -
/v3/kv/put
- Body -
{"key":"mykey","value":"myvalue"}
这个请求是v3版本的,而v2版本的差异比较大,暂不细谈。
Mux的路由匹配
背景知识介绍
为了更好地介绍下面的内容,我先介绍mux下的2个概念。
pattern
指的是一种URL的匹配模式,最常见的如全量匹配、前缀匹配、正则匹配。当一个请求进来时,它会有自己的一个URL,去匹配mux
中预先定义的多个pattern
,找到一个最合适的。这是一种URL路由规则的实现。- 当请求匹配到一个
pattern
后,就会执行它预定义的handler
,也就是一个处理函数,返回结果。
所以, pattern
负责匹配,而handler
负责执行。在不同语境下,它们的专业术语有所差异,大家自行对应即可。
http mux的创建
我们要找HTTP1.X的路由匹配逻辑,就回到了上一节最后看到的代码中:
1 | // 创建路由匹配规则 |
(*serveCtx)createMux
本函数不长,但很容易让读源码的同学陷入误区,我们一起来看看。这块代码主要分为三段:
1 | func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.ServeMux { |
第一点,可以通过简单的代码阅读,看到是对pprof
和debug
这些通用功能的URL功能注册,也是一些用户自定义的handler
注册,这就很好地对应到sctx.userHandlers
这个变量的命名了。
第三点很快就能被排除,它注册的是对根路径下的handler。我们阅读代码,找到handler最原始的生成处,就能看到它是对version、metrcis这类handler的注册。
所以,我们的重点就放在了gwmux
这个对象上。阅读它的创建过程,就得跳转到上层函数。
(*serveCtx)registerGateway
在函数中,我们可以看到它注册了一个类型为registerHandlerFunc
的handlers列表,包括如下内容:
1 | handlers := []registerHandlerFunc{ |
我们聚焦到PUT请求的处理,它自然走的是etcdservergw.RegisterKVHandler
这个入口。
RegisterKVHandler
本函数位于etcd/etcdserver/etcdserverpb/gw/rpc.pb.gw.go
。它其实是用protobuf自动生成的,其中用到了grpc-gateway
这个关键性技术,它的作用是将HTTP1的请求转换成gRPC,实现一个server可以同时支持HTTP1与gRPC,并且只写一份gRPC处理的代码即可。
有兴趣地可以去看看 https://github.com/grpc-ecosystem/grpc-gateway 项目。
大致调用链路为: HTTP1 -> gRPC -> 自己实现的handler
RegisterKVHandlerClient
该函数是由proto文件生成的,这里我忽略了关于context的处理,提取关键性的内容:
1 | mux.Handle("POST", pattern_KV_Put_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { |
序列化与反序列化存在多种选择,我们暂不深入,先来看看处理这部分的工作:
首先是如何匹配请求,也就是http://localhost:2379/v3/kv/put
,对应如下:
1 | var pattern_KV_Put_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "put"}, "")) |
而最核心的处理,也就是解析PUT请求的函数request_KV_Put_0
与返回处理结果的函数forward_KV_Put_0
,我们放到下一讲再来看。
小结
今天我们看了PUT
请求在etcd server中通过mux
的匹配逻辑,思路参考下图。
在阅读代码期间,我们接触到了grpc-gateway这个技术方案,有兴趣的朋友可以参考我的另一篇文章。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding