/* 使用了第三方 github.com/armon/go-proxyproto 包 @todo 两次调用了 ln.Close() 为什么不会引发问题? defer ln.Close() // close the socket on exit to terminate the accept loop go func() { <-quit ln.Close() }() */ func listenAndServeTCP(l config.Listen, h proxy.TCPProxy) { log.Print("[INFO] TCP+SNI proxy listening on ", l.Addr) // 生成 Listener 结构体类型 ln, err := net.Listen("tcp", l.Addr) if err != nil { exit.Fatal("[FATAL] ", err) } ln = &proxyproto.Listener{Listener: tcpKeepAliveListener{ln.(*net.TCPListener)}} defer ln.Close() // close the socket on exit to terminate the accept loop go func() { <-quit ln.Close() }() for { // 接收连接请求 conn, err := ln.Accept() if err != nil { select { case <-quit: return default: exit.Fatal("[FATAL] ", err) } } // 处理连接 go h.Serve(conn) } }
func listenAndServe(l config.Listen, h http.Handler) { srv := &http.Server{ Handler: h, Addr: l.Addr, ReadTimeout: l.ReadTimeout, WriteTimeout: l.WriteTimeout, } if l.Scheme == "https" { src, err := cert.NewSource(l.CertSource) if err != nil { exit.Fatal("[FATAL] ", err) } srv.TLSConfig, err = cert.TLSConfig(src) if err != nil { exit.Fatal("[FATAL] ", err) } } if srv.TLSConfig != nil { log.Printf("[INFO] HTTPS proxy listening on %s", l.Addr) if srv.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert { log.Printf("[INFO] Client certificate authentication enabled on %s", l.Addr) } } else { log.Printf("[INFO] HTTP proxy listening on %s", l.Addr) } if err := serve(srv); err != nil { exit.Fatal("[FATAL] ", err) } }
func initMetrics(cfg *config.Config) { if cfg.Metrics.Target == "" { log.Printf("[INFO] Metrics disabled") return } var err error if metrics.DefaultRegistry, err = metrics.NewRegistry(cfg.Metrics); err != nil { exit.Fatal("[FATAL] ", err) } if route.ServiceRegistry, err = metrics.NewRegistry(cfg.Metrics); err != nil { exit.Fatal("[FATAL] ", err) } }
// NewRegistry creates a new metrics registry. func NewRegistry(cfg config.Metrics) (r Registry, err error) { if prefix, err = parsePrefix(cfg.Prefix); err != nil { return nil, fmt.Errorf("metrics: invalid Prefix template. %s", err) } if names, err = parseNames(cfg.Names); err != nil { return nil, fmt.Errorf("metrics: invalid names template. %s", err) } switch cfg.Target { case "stdout": log.Printf("[INFO] Sending metrics to stdout") return gmStdoutRegistry(cfg.Interval) case "graphite": log.Printf("[INFO] Sending metrics to Graphite on %s as %q", cfg.GraphiteAddr, prefix) return gmGraphiteRegistry(prefix, cfg.GraphiteAddr, cfg.Interval) case "statsd": log.Printf("[INFO] Sending metrics to StatsD on %s as %q", cfg.StatsDAddr, prefix) return gmStatsDRegistry(prefix, cfg.StatsDAddr, cfg.Interval) case "circonus": return circonusRegistry(prefix, cfg.Circonus, cfg.Interval) default: exit.Fatal("[FATAL] Invalid metrics target ", cfg.Target) } panic("unreachable") }
func defaultPrefix() string { host, err := hostname() if err != nil { exit.Fatal("[FATAL] ", err) } exe := filepath.Base(os.Args[0]) return clean(host) + "." + clean(exe) }
func startAdmin(cfg *config.Config) { log.Printf("[INFO] Admin server listening on %q", cfg.UI.Addr) go func() { if err := admin.ListenAndServe(cfg, version); err != nil { exit.Fatal("[FATAL] ui: ", err) } }() }
func initBackend(cfg *config.Config) { var err error switch cfg.Registry.Backend { case "file": registry.Default, err = file.NewBackend(cfg.Registry.File.Path) case "static": registry.Default, err = static.NewBackend(cfg.Registry.Static.Routes) case "consul": registry.Default, err = consul.NewBackend(&cfg.Registry.Consul) default: exit.Fatal("[FATAL] Unknown registry backend ", cfg.Registry.Backend) } if err != nil { exit.Fatal("[FATAL] Error initializing backend. ", err) } if err := registry.Default.Register(); err != nil { exit.Fatal("[FATAL] Error registering backend. ", err) } }
func newHTTPProxy(cfg *config.Config) http.Handler { if err := route.SetPickerStrategy(cfg.Proxy.Strategy); err != nil { exit.Fatal("[FATAL] ", err) } log.Printf("[INFO] Using routing strategy %q", cfg.Proxy.Strategy) if err := route.SetMatcher(cfg.Proxy.Matcher); err != nil { exit.Fatal("[FATAL] ", err) } log.Printf("[INFO] Using routing matching %q", cfg.Proxy.Matcher) tr := &http.Transport{ ResponseHeaderTimeout: cfg.Proxy.ResponseHeaderTimeout, MaxIdleConnsPerHost: cfg.Proxy.MaxConn, Dial: (&net.Dialer{ Timeout: cfg.Proxy.DialTimeout, KeepAlive: cfg.Proxy.KeepAliveTimeout, }).Dial, } return proxy.NewHTTPProxy(tr, cfg.Proxy) }
func serve(srv *http.Server) error { ln, err := net.Listen("tcp", srv.Addr) if err != nil { exit.Fatal("[FATAL] ", err) } ln = &proxyproto.Listener{Listener: tcpKeepAliveListener{ln.(*net.TCPListener)}} if srv.TLSConfig != nil { ln = tls.NewListener(ln, srv.TLSConfig) } return srv.Serve(ln) }
/** 使用配置信息创建并返回HTTP代理服务器的句柄 */ func newHTTPProxy(cfg *config.Config) http.Handler { // 设置路由拣选策略 if err := route.SetPickerStrategy(cfg.Proxy.Strategy); err != nil { exit.Fatal("[FATAL] ", err) } log.Printf("[INFO] Using routing strategy %q", cfg.Proxy.Strategy) // 设置路由匹配器 if err := route.SetMatcher(cfg.Proxy.Matcher); err != nil { exit.Fatal("[FATAL] ", err) } log.Printf("[INFO] Using routing matching %q", cfg.Proxy.Matcher) // 配置转换器 tr := &http.Transport{ ResponseHeaderTimeout: cfg.Proxy.ResponseHeaderTimeout, MaxIdleConnsPerHost: cfg.Proxy.MaxConn, Dial: (&net.Dialer{ Timeout: cfg.Proxy.DialTimeout, KeepAlive: cfg.Proxy.KeepAliveTimeout, }).Dial, } /** @todo 上面代码中有疑问,如下代码: Dial: (&net.Dialer{ Timeout: cfg.Proxy.DialTimeout, KeepAlive: cfg.Proxy.KeepAliveTimeout, }).Dial 第一行为何用 &net.Dialer ? 即为何使用引用? 原因是 net包的Dialer结构体(struct)的方法Dial是指针类型,所以只有使用引用定义的时候才能访问到该函数 */ // 生成并返回HTTP代理句柄 return proxy.NewHTTPProxy(tr, cfg.Proxy) }
func Init(cfg config.Metrics) error { pfx = cfg.Prefix if pfx == "default" { pfx = defaultPrefix() } switch cfg.Target { case "stdout": log.Printf("[INFO] Sending metrics to stdout") return initStdout(cfg.Interval) case "graphite": if cfg.GraphiteAddr == "" { return errors.New("metrics: graphite addr missing") } log.Printf("[INFO] Sending metrics to Graphite on %s as %q", cfg.GraphiteAddr, pfx) return initGraphite(cfg.GraphiteAddr, cfg.Interval) case "": log.Printf("[INFO] Metrics disabled") default: exit.Fatal("[FATAL] Invalid metrics target ", cfg.Target) } return nil }
func initMetrics(cfg *config.Config) { if err := metrics.Init(cfg.Metrics); err != nil { exit.Fatal("[FATAL] ", err) } }