router详解
路由就是负责将entrypoints上的请求转发到对应的服务,同时还会根据配置使用中间价更新请求,下面我们看看一个最基础的router,配置如下
为了方便调试,采用的file方式,一般线上我们为配置中心/k8s ingress使用
#通过providers.file.directory加载进去
http:
routers:
test1:
entryPoints:
- "ep1"
rule: "Path(`/foo`)"
service: "svc1"
services:
svc1:
loadBalancer:
servers:
- url: "http://127.0.0.1:9998/"
- url: "http://127.0.0.1:9999/"
routerFactory管理着tcp(包括http),udp路由,插件等等,watcher为配置的动态更新,后边有专门章节讲解,本文主要分析entrypoint对应的路由设置
routerFactory主要包含以下几个模块
- staticConfiguration:配置相关
- managerFactory
- routinesPool: goroutine池,
- observabilityMgr: 指标上报,监控相关
- roundTripperManager:负载均衡管理(有专门章节讲解)
- tlsManager:tls相关
- acmeHTTPHandler:acme证书相关,(后续专文分析)
- routerFactory参数基本类似 watcher.AddListener当配置更新时,重新执行switchRouter的逻辑,在后面配置更新里分析
//cmd/traefik/traefik.go
//根据EntryPoints构建RouterFactory.entryPointsTCP = []{entryPoints.name1,entryPoints.name2}
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
// watcher.AddListener监听更新配置,后续providers分析,
//switchRouter 根据配置设置tcp/udp路由,本文只分析tcp路由
watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP))
//cmd/traefik/traefik.go
routers, udpRouters := routerFactory.CreateRouters(rtConf)
//设置路由,配置一般指向service,service可对接注册中心实现动态负载均衡
serverEntryPointsTCP.Switch(routers)
switchRouter
switchRouter函数主要功能为更新router,配合watcher动态更新,runtime.NewConfig将配置转换为内部运行时配置
通过createRouter创建具体路由,并绑定到entrypoints上,具体的逻辑在routerManager.BuildHandlers里
//cmd/traefik/traefik.go
func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) {
return func(conf dynamic.Configuration) {
//获取http,tcp,udp router services配置转换成traefik对应的runtime.conf
rtConf := runtime.NewConfig(conf)
//根据配置创建路由
routers, udpRouters := routerFactory.CreateRouters(rtConf)
serverEntryPointsTCP.Switch(routers)
serverEntryPointsUDP.Switch(udpRouters)
}
}
// CreateRouters creates new TCPRouters and UDPRouters.
func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string]*tcprouter.Router, map[string]udp.Handler) {
//ctx.cancel()
if f.cancelPrevState != nil {
f.cancelPrevState()
}
var ctx context.Context
ctx, f.cancelPrevState = context.WithCancel(context.Background())
//根据配置创建services,下面章节services分析,包含了配置以及负载均衡相关
serviceManager := f.managerFactory.Build(rtConf)
//根据配置创建中间件
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder)
//路由管理
routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.observabilityMgr, f.tlsManager)
//创建handler,router的主要逻辑
handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false)
handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true)
//。。。。。。
//省略了tcp,udp路由创建,本文暂时跳过
//健康检查
serviceManager.LaunchHealthCheck(ctx)
return routersTCP, routersUDP
}
router.NewManager
为每个entryPoint创建路由具体的逻辑,通过m.getHTTPRouters获取所有的entryPoint对应的路由配置,然后通过buildEntryPointHandler方法
将router与services结合起来,同时添加日志中间件
//pkg/server/router/router.go
func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, tls bool) map[string]http.Handler {
entryPointHandlers := make(map[string]http.Handler)
for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) {
entryPointName := entryPointName
ctx := logger.WithContext(rootCtx)
//buildEntryPointHandler就是根据配置创建路由具体方法
handler, err := m.buildEntryPointHandler(ctx, entryPointName, routers)
entryPointHandlers[entryPointName] = handler
}
//省略为每个entryPoint创建404默认路由代码
return entryPointHandlers
}
//pkg/server/router/router.go
func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, routerConfig *runtime.RouterInfo) (http.Handler, error) {
//构建路由handler,
handler, err := m.buildHTTPHandler(ctx, routerConfig, routerName)
//添加日志中间件
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil
}).Then(handler)
m.routerHandlers[routerName] = handlerWithAccessLog
return m.routerHandlers[routerName], nil
}
//pkg/server/router/router.go
func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterInfo, routerName string) (http.Handler, error) {
//中间件相关
for _, name := range router.Middlewares {
qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(ctx, name))
}
router.Middlewares = qualifiedNames
//将路由与services结合起来,下节services再分析
sHandler, err := m.serviceManager.BuildHTTP(ctx, router.Service)
//中间件构建调用链
mHandler := m.middlewaresBuilder.BuildChain(ctx, router.Middlewares)
//省略了部分trace,指标上报相关
//最后返回一条调用链构造好的handler
return chain.Extend(*mHandler).Then(sHandler)
}