github.com/cnotch/ipchub@v1.1.0/service/service.go (about) 1 // Copyright (c) 2019,CAOHONGJU All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package service 6 7 import ( 8 "context" 9 "crypto/tls" 10 "fmt" 11 "net" 12 "net/http" 13 "net/http/pprof" 14 "os" 15 "os/signal" 16 "syscall" 17 "time" 18 19 "github.com/cnotch/ipchub/config" 20 "github.com/cnotch/ipchub/media" 21 "github.com/cnotch/ipchub/network/socket/listener" 22 "github.com/cnotch/ipchub/provider/auth" 23 "github.com/cnotch/ipchub/provider/route" 24 "github.com/cnotch/ipchub/service/rtsp" 25 "github.com/cnotch/scheduler" 26 "github.com/cnotch/xlog" 27 "github.com/emitter-io/address" 28 "github.com/kelindar/tcp" 29 ) 30 31 // Service 网络服务对象(服务的入口) 32 type Service struct { 33 context context.Context 34 cancel context.CancelFunc 35 logger *xlog.Logger 36 tlsusing bool 37 http *http.Server 38 rtsp *tcp.Server 39 tokens *auth.TokenManager 40 } 41 42 // NewService 创建服务 43 func NewService(ctx context.Context, l *xlog.Logger) (s *Service, err error) { 44 ctx, cancel := context.WithCancel(context.Background()) 45 s = &Service{ 46 context: ctx, 47 cancel: cancel, 48 logger: l, 49 http: new(http.Server), 50 rtsp: new(tcp.Server), 51 tokens: new(auth.TokenManager), 52 } 53 54 // 设置 http 的Handler 55 mux := http.NewServeMux() 56 57 // 管理员控制台 58 if consoleAppDir, ok := config.ConsoleAppDir(); ok { 59 mux.Handle("/", http.FileServer(http.Dir(consoleAppDir))) 60 } 61 62 // Demo应用 63 if demosAppDir, ok := config.DemosAppDir(); ok { 64 mux.Handle("/demos/", http.StripPrefix("/demos/", http.FileServer(http.Dir(demosAppDir)))) 65 } 66 67 if config.Profile() { 68 mux.HandleFunc("/debug/pprof/", pprof.Index) 69 mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 70 mux.HandleFunc("/debug/pprof/profile", pprof.Profile) 71 mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 72 mux.HandleFunc("/debug/pprof/trace", pprof.Trace) 73 } 74 75 s.initApis(mux) 76 s.initHTTPStreams(mux) 77 s.http.Handler = mux 78 79 // 设置 rtsp AcceptHandler 80 s.rtsp.OnAccept = rtsp.CreateAcceptHandler() 81 82 // 启动定时存储拉流信息 83 scheduler.PeriodFunc(time.Minute*5, time.Minute*5, func() { 84 route.Flush() 85 auth.Flush() 86 s.tokens.ExpCheck() 87 }, "The task of scheduled storage of routing tables and authorization information tables(5minutes") 88 89 s.logger.Info("service configured") 90 return s, nil 91 } 92 93 // Listen starts the service. 94 func (s *Service) Listen() (err error) { 95 defer s.Close() 96 s.hookSignals() 97 98 // http rtsp ws 99 addr, err := address.Parse(config.Addr(), 554) 100 if err != nil { 101 s.logger.Panic(err.Error()) 102 } 103 104 s.listen(addr, nil) 105 106 // https wss 107 tlsconf := config.GetTLSConfig() 108 if tlsconf != nil { 109 tls, err := tlsconf.Load() 110 if err == nil { 111 if tlsAddr, err := address.Parse(tlsconf.ListenAddr, 443); err == nil { 112 s.listen(tlsAddr, tls) 113 s.tlsusing = true 114 } 115 } 116 } 117 118 s.logger.Infof("service started(%s).", config.Version) 119 s.logger = xlog.L() 120 // Block 121 select {} 122 } 123 124 // listen configures an main listener on a specified address. 125 func (s *Service) listen(addr *net.TCPAddr, conf *tls.Config) { 126 // Create new listener 127 s.logger.Infof("starting the listener, addr = %s.", addr.String()) 128 129 l, err := listener.New(addr.String(), conf) 130 if err != nil { 131 s.logger.Panic(err.Error()) 132 } 133 134 // Set the read timeout on our mux listener 135 timeout := time.Duration(int64(config.NetTimeout()) / 3) 136 l.SetReadTimeout(timeout) 137 138 // Set Error handler 139 l.HandleError(listener.ErrorHandler(func(err error) bool { 140 xlog.Warn(err.Error()) 141 return true 142 })) 143 144 // Configure the matchers 145 l.ServeAsync(rtsp.MatchRTSP(), s.rtsp.Serve) 146 l.ServeAsync(listener.MatchHTTP(), s.http.Serve) 147 go l.Serve() 148 } 149 150 // Close closes gracefully the service., 151 func (s *Service) Close() { 152 if s.cancel != nil { 153 s.cancel() 154 } 155 156 // 停止计划任务 157 jobs := scheduler.Jobs() 158 for _, job := range jobs { 159 job.Cancel() 160 } 161 162 // 清空注册 163 media.UnregistAll() 164 // 退出前确保最新数据被存储 165 route.Flush() 166 auth.Flush() 167 } 168 169 // OnSignal starts the signal processing and makes su 170 func (s *Service) hookSignals() { 171 c := make(chan os.Signal, 1) 172 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 173 go func() { 174 for sig := range c { 175 s.onSignal(sig) 176 } 177 }() 178 } 179 180 // OnSignal will be called when a OS-level signal is received. 181 func (s *Service) onSignal(sig os.Signal) { 182 switch sig { 183 case syscall.SIGTERM: 184 fallthrough 185 case syscall.SIGINT: 186 s.logger.Warn(fmt.Sprintf("received signal %s, exiting...", sig.String())) 187 s.Close() 188 os.Exit(0) 189 } 190 }