GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)

荔枝岛岛主
发布于 2023-10-7 11:08
浏览
0收藏

本地启动

在第三篇自定义中间件的前提下,已经说了很多关于本地通过 CRD 或者 File 作为 provider 的启动方式,这里想要补充一下关于本地 DEBUG 3.0.0 版本代码的问题,后续的源码分析也都会基于目前最新的 3.0 版本。

在写文章的时候,目前 3.0 版本还是 beta 版本。

按照上述文章的方式安装之后其实会发现本地启动会报一个关于​​*v1alpha1.ServersTransportTCP​​相关的错误,这是因为之前我们安装的 CRD 资源定义和 RBAC 都是 2.10 版本的,需要重新安装一下 3.0.0 版本的资源定义。

# Install Traefik Resource Definitions:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml

# Install RBAC for Traefik:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml

启动流程

Traefik 启动的源码在​​cmd/traefik/traefik.go​​中,进入到 main 方法。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

我们在本地 DEBUG 就是启动这个 main 方法,首先进行配置的初始化,然后定义了 3 个资源加载的方式,和官方描述的一样,可以从配置文件、命令行参数和环境变量读取配置。

之后就是添加启动命令,包括健康检测、版本检查,最后回去调用​​cli.Execute​​​方法,这个方法跟进去看也挺简单的,最终其实都会调用到​​Command​​​的​​Run​​​方法,也就是会执行​​runCmd​​方法。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入 Execute 方法,我们启动是没有其他参数的,只是启动命令,所以进入第一个判断,参数长度为1。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

然后最终跟我们上面说的一样,调用到了​​cmd.Run​​。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

然后直接进入​​runCmd​​方法中看服务如何启动的。

这里干了几件事情,首先是初始化日志的配置、静态配置,然后解析静态配置为 json,然后就是很关键的两个步骤,一个是​​setupServer​​初始化 server,之后是 server 的启动。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里可以看一下静态配置到底都有些什么东西,因为我们没有配置 AcessLog、Metric 这些东西,所以都是空的,其实关键就两个东西,一个是 EntryPoints,另外一个是 Provider。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里我们本地启动通过 CRD 的方式,入口点只是默认的配置。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

Server初始化

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

然后我们直接看 ​​setupServer​​都干了些什么,这个方法非常长,我们忽略调一些无关的细节,只看重点的部分。

首先创建 provider 的聚合器,其实就是服务发现的提供者,我们知道有很多,像是文件、Docker、K8S、ECS、Consul 等等很多,在之前我们就提到过这个,这里我们主要是 CRD。

ACME 是 HTTPS 证书相关的东西,可以忽略他。

然后是关于​​server.NewTCPEntryPoints​​,用于构建入口点的逻辑,这个很关键。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

接着是创建 Provider 的插件,这个​​X509Source​​又是关于证书的一些逻辑,主要是使用 SPIFFE (Secure Production Identity Framework For Everyone) 获取证书,以便与其他服务进行安全通信,这个不是重点,忽略。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

接着往下走是几个比较关键的 Factory,一个是​​managerFactory​​​,另外一个则是​​routerFactory​​,这两个工厂会构建出后续请求的流程链路,放到后面再说。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里的 watcher 也是一个关键点,添加了关于 TLS、Metrics、Server Transports 和 Switch Router 的动态配置的监听。

这里的​​switchRouter​​是一个很重要的方法,待会儿再细看。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

最终流程走完到最终执行​​NewServer​​方法创建 Server,配置初始化 Server 的流程结束。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

配置监听

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这个流程看完之后,回到最开始的地方,开始启动 Server。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里主要关注两个方法,一个是 TCP 入口点的启动,另外一个则是监听 watcher 的启动,首先看​​tcpEntryPoints.Start​​。

这里可以看到入口点默认是两个,一个是 traefik 自己默认的,另外一个是处理 web 请求的入口点,之后开启协程进入 ​​serverEntryPoint.Start(ctx)​​。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里主要是监听连接,当有新连接到达时,创建一个新的 ​writeCloser​ 对象,并在其上设置读/写超时。接下来,使用 ​e.switcher.ServeTCP​ 处理新连接,如果出现错误,则会记录错误日志并将错误转发到 ​e.httpServer​ 和 ​e.httpsServer​ 的 channel。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

之后就进入 watcher 监听的启动方法,主要实现了 3 个方法。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

1. receiveConfigurations 是用于接收配置的变化,并且发送到消费者

2. applyConfigurations 是合并配置、应用配置

3. 启动 Provider 的聚合器,做服务发现

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

​receiveConfigurations​​主要是死循环监听配置的变更,新的配置会发送到 output 的 channel。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

​applyConfigurations​​ 则是收到新的配置之后,合并然后应用配置,具体合并的细节就不看了,忽略他。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

provider 的聚合器是根据不同的 provider 提供了不同的实现,这里我们是使用 K8S CRD 的方式,所以待会儿进去看这个实现即可。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

首先创建一个 K8S client 客户端,用于和 K8S 集群进行通信,然后使用该客户端创建一个事件通道。

接下来,使用​​WatchAll​​监听所有配置的 namespace 的变化。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里 debug 能看到现在我们只配置了一个默认的 namespace。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

在第一次启动的时候,默认调用​​loadConfigurationFromCRD​​​方法从 CRD 加载配置,之后如果配置发生变化,走到 default 分支,变更的配置会发送到 ​​configurationChan​​。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这个 channel 的定义其实就是在方法入口的​​ConfigurationWatcher​​。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

查看 ConfigurationWatcher 结构体的定义,找到​​allProvidersConfigs​​,定义的就是我们发送到的 channel 了。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

看到这里,会有疑问,那么发送的配置在什么地方进行消费了呢?

答案就是上面我们已经讲过的​​receiveConfigurations​​​,而​​receiveConfigurations​​​消费到配置变更之后,又会发送消息到​​newConfigs​​​,然后​​applyConfigurations​​方法进行消费,然后处理配置、进行合并、应用。

服务发现

然后让我们回到 ​​loadConfigurationFromCRD​​​这个方法中,找到​​loadIngressRouteConfiguration​​方法,这里就是启动的时候去做服务发现的地方。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入这个方法就能看到通过 K8S 的客户端去获取配置的 IngressRoute。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入这个方法,发现也是确实如此。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

这里 DEBUG 可以看到我们日志的 IngressRoute 的信息。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

往下看还有一个比较有意思的地方,如果说路由中配置的 service 数量超过 1,那么会默认会变成权重负载均衡。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

接着往下看​​buildServicesLB​​​的方法,其实和 service==1 的情况一样,只是循环去调用了​​nameAndService​​方法而已。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入这个方法这里有两个判断,针对类型是 Service 和 TraefikService的,Service 类型是 K8S 的类型,所以会去直接获取 server 的信息,而如果配置的是 TraefikService,实际上只是根据 TraefikService 获取 Service 名字,最终还是会走到 Service 的判断逻辑上。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

创建负载均衡代码的关键在于第一行​​loadServers​​。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入这个方法可以看到其实就是通过 K8S 的客户端去调用 API 接口,获取到了 service 的信息,还有端口、后端服务的地址。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

进入这个方法可以看到其实就是通过 K8S 的客户端去调用 API 接口,获取到了 service 的信息,还有端口、后端服务的地址。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区

那么,到这里基本服务发现的逻辑就结束了。


在这里顺便说点事情,最近可能大家觉得公共号没啥更新了,其实情况并不是这样。

首先一点确实是公众号目前情况不佳,阅读下滑,这个也就那样吧。

另外一点是最近几个月的重心有工作、生活两方面吧。

工作研究性质的东西会比较多一点,比如最近在学 GO,研究 Traefik,所以就写了 Traefik 的一个专栏,这个玩意儿如果想在生产使用看我这个肯定没啥问题,你在网上几乎都搜不到像样的文章,这东西我们研究了好几个月了,所以我才能写出来。

而文章更新因为有了星球,所以内容会偏向星球居多一点。

当然你要说就想免费的也行啊,但是大家都知道免费的其实最贵。

还有一点是包括抖音等平台的视频更新,虽然不多,但是也有快2万粉丝了,这个我几乎佛系更新的情况下居然这么猛是我没想到的,也欢迎大家关注下。

SO,我欢迎大家不缺个100的话,可以加入星球,查看完整 Traefik 云原生网关系列,目前我已经更新了两个专栏,Traefik 系列也即将进入尾声,Q4还会有持续的更新计划。

其他的一些内容是我之前写的文章重新整理,还有后续的补充,都会在里面。

嗯,大概就是这样,我始终相信真正有价值的东西不需要太多的营销话术,仅仅是信任就行了,你懂我不会吹。

OK,就这样,感谢大家。

GO 学了一个月,代码不会写,先看个源码解析(文末说点事儿)-鸿蒙开发者社区





文章转载自公众号:艾小仙

分类
已于2023-10-7 11:08:15修改
收藏
回复
举报
回复
    相关推荐