router详解

routers.png 路由就是负责将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)
}