traefik
go语言实现的api网关、具备路由热更新、熔断、限流的功能
github: traefik
本次源码分析基于2.1.3
traefik 配合分为静态配置和动态配置
- 静态配置:在运行期间不会改变的配置,比如监听的端口、日志配置等
- 动态配置:在运行期间会改变的配置,traefik会定时拉取更新,比如路由配置、限流和熔断的配置等
静态配置加载解析
cmd/traefik/traefik.go
func main() {
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
// 这里支持三种静态配置的加载方式:file、flag、env 我们这里以文件方式来举例分析,其他的两种方式大家可以自己阅读源码,相对难度较小
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
cmdTraefik := &cli.Command{
Name: "traefik",
Description: `Traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at https://traefik.io`,
Configuration: tConfig,
Resources: loaders,
Run: func(_ []string) error {
return runCmd(&tConfig.Configuration)
},
}
err := cmdTraefik.AddCommand(healthcheck.NewCmd(&tConfig.Configuration, loaders))
if err != nil {
stdlog.Println(err)
os.Exit(1)
}
err = cmdTraefik.AddCommand(cmdVersion.NewCmd())
if err != nil {
stdlog.Println(err)
os.Exit(1)
}
// 这里traefik的启动是以命令的方式来执行的
err = cli.Execute(cmdTraefik)
if err != nil {
stdlog.Println(err)
logrus.Exit(1)
}
logrus.Exit(0)
}
pkg/cli/commands.go
func run(cmd *Command, args []string) error {
...
// 这里面会遍历三种静态配置加载方式,如果一个加载成功,则返回,下面我们主要看下file 方式静态配置的加载方式
// resource.Load 最终调用pkg/cli/loader_file.go(file方式)
for _, resource := range cmd.Resources {
done, err := resource.Load(args, cmd)
if err != nil {
return err
}
if done {
break
}
}
return cmd.Run(args)
}
pkg/cli/loader_file.go
func (f *FileLoader) Load(args []string, cmd *Command) (bool, error) {
...
// 这个地方读取配置文件, 这里返回的是配置文件的路径,同时在内部实现了文件内容解析
// 具体实现在下面的loadConfigFiles函数
configFile, err := loadConfigFiles(ref[configFileFlag], cmd.Configuration)
if err != nil {
return false, err
}
f.filename = configFile
if configFile == "" {
return false, nil
}
logger := log.WithoutContext()
logger.Printf("Configuration loaded from file: %s", configFile)
content, _ := ioutil.ReadFile(configFile)
logger.Debug(string(content))
return true, nil
}
// loadConfigFiles tries to decode the given configuration file and all default locations for the configuration file.
// It stops as soon as decoding one of them is successful.
func loadConfigFiles(configFile string, element interface{}) (string, error) {
// 静态文件搜索目录和顺序
finder := Finder{
BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"},
Extensions: []string{"toml", "yaml", "yml"},
}
filePath, err := finder.Find(configFile)
if err != nil {
return "", err
}
if len(filePath) == 0 {
return "", nil
}
// 这里是解析文件内容生成配置的地方
// 最终调用pkg/config/file/file.go
if err = file.Decode(filePath, element); err != nil {
return "", err
}
return filePath, nil
}
pkg/config/file/file.go
这里是文件解析的具体代码,已toml文件为例 首先解析toml文件为map, 然后使用反射将配置解析出来,这块代码如果大家感兴趣,可以自己阅读,难度不大。 同时,因为配置解析是很多项目的必备模块,大家以后设计配置解析的时候,可以参考这块的实现方式
func Decode(filePath string, element interface{}) error {
if element == nil {
return nil
}
filters := getRootFieldNames(element)
root, err := decodeFileToNode(filePath, filters...)
if err != nil {
return err
}
err = parser.AddMetadata(element, root)
if err != nil {
return err
}
return parser.Fill(element, root)
}
动态配置
篇幅有点长,下一篇单独写吧
编程、面试交流
