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  }