github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"os/signal"
    10  	"syscall"
    11  
    12  	"github.com/spf13/cobra"
    13  	"github.com/vicanso/pike/app"
    14  	"github.com/vicanso/pike/cache"
    15  	"github.com/vicanso/pike/compress"
    16  	"github.com/vicanso/pike/config"
    17  	"github.com/vicanso/pike/location"
    18  	"github.com/vicanso/pike/log"
    19  	_ "github.com/vicanso/pike/schedule"
    20  	"github.com/vicanso/pike/server"
    21  	"github.com/vicanso/pike/store"
    22  	"github.com/vicanso/pike/upstream"
    23  	"go.uber.org/automaxprocs/maxprocs"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  var (
    28  	version = "dev"
    29  	commit  = "none"
    30  	date    = "unknown"
    31  	builtBy = "unknown"
    32  )
    33  
    34  // alarmURL 告警发送的地址
    35  var alarmURL string
    36  var alarmTemplate string
    37  
    38  func init() {
    39  
    40  	err := runCMD()
    41  	if err != nil {
    42  		panic(err)
    43  	}
    44  	// 如果是help cmd,则
    45  	if isHelpCmd() {
    46  		os.Exit(0)
    47  		return
    48  	}
    49  	_, _ = maxprocs.Set(maxprocs.Logger(func(format string, args ...interface{}) {
    50  		value := fmt.Sprintf(format, args...)
    51  		log.Default().Info(value)
    52  	}))
    53  	app.SetBuildInfo(date, commit, version, builtBy)
    54  	hostname, _ := os.Hostname()
    55  	alarmTemplate = `{
    56  		"application": "pike",
    57  		"hostname": "` + hostname + `",
    58  		"category": "%s",
    59  		"message": "%s"
    60  	}`
    61  }
    62  
    63  // doAlarm 发送告警
    64  func doAlarm(category, message string) {
    65  	if alarmURL == "" {
    66  		return
    67  	}
    68  	data := fmt.Sprintf(alarmTemplate, category, message)
    69  	resp, err := http.Post(alarmURL, "application/json", bytes.NewBufferString(data))
    70  	if err != nil {
    71  		log.Default().Error("do alarm fail",
    72  			zap.Error(err),
    73  		)
    74  		return
    75  	}
    76  	defer resp.Body.Close()
    77  	result, _ := ioutil.ReadAll(resp.Body)
    78  	if resp.StatusCode >= 400 {
    79  		log.Default().Error("do alarm fail",
    80  			zap.Int("status", resp.StatusCode),
    81  			zap.String("result", string(result)),
    82  		)
    83  	}
    84  }
    85  
    86  func update() (err error) {
    87  	pikeConfig, err := config.Read()
    88  	if err != nil {
    89  		return
    90  	}
    91  	// 重置压缩列表
    92  	compress.Reset(pikeConfig.Compresses)
    93  	// 重置默认dispatcher列表
    94  	cache.ResetDispatchers(pikeConfig.Caches)
    95  	// 重置默认的upstream列表
    96  	upstream.ResetWithOnStats(pikeConfig.Upstreams, func(si upstream.StatusInfo) {
    97  		log.Default().Info("upstream status change",
    98  			zap.String("name", si.Name),
    99  			zap.String("status", si.Status),
   100  			zap.String("addr", si.URL),
   101  		)
   102  
   103  		if si.Status == "sick" {
   104  			message := fmt.Sprintf("%s is %s, addr: %s", si.Name, si.Status, si.URL)
   105  			go doAlarm("upstream", message)
   106  		}
   107  	})
   108  	// 重置location列表
   109  	location.Reset(pikeConfig.Locations)
   110  
   111  	server.Reset(pikeConfig.Servers)
   112  	return server.Start()
   113  }
   114  
   115  func startAdminServer(addr string) error {
   116  	pikeConfig, err := config.Read()
   117  	if err != nil {
   118  		return err
   119  	}
   120  	return server.StartAdminServer(server.AdminServerConfig{
   121  		Addr:     addr,
   122  		User:     pikeConfig.Admin.User,
   123  		Password: pikeConfig.Admin.Password,
   124  	})
   125  }
   126  
   127  // runCMD 解析各命令参数
   128  func runCMD() error {
   129  	configURL := ""
   130  	adminAddr := ""
   131  	logOutputPath := ""
   132  
   133  	var rootCmd = &cobra.Command{
   134  		Use:   "pike",
   135  		Short: "Pike is a http cache server",
   136  		PreRun: func(cmd *cobra.Command, args []string) {
   137  			if logOutputPath != "" {
   138  				log.SetOutputPath(logOutputPath)
   139  			}
   140  			// 初始化配置
   141  			err := config.InitDefaultClient(configURL)
   142  			if err != nil {
   143  				panic(err)
   144  			}
   145  		},
   146  		Run: func(cmd *cobra.Command, args []string) {
   147  
   148  			if adminAddr != "" {
   149  				go func() {
   150  					err := startAdminServer(adminAddr)
   151  					if err != nil {
   152  						log.Default().Error("start admin server fail",
   153  							zap.String("addr", adminAddr),
   154  							zap.Error(err),
   155  						)
   156  						go doAlarm("admin", adminAddr+", "+err.Error())
   157  					}
   158  				}()
   159  			}
   160  			run()
   161  		},
   162  	}
   163  	// 配置文件地址
   164  	rootCmd.Flags().StringVar(&configURL, "config", "pike.yml", "The config of pike, support etcd or file, etcd://user:pass@192.168.1.2:2379,192.168.1.3:2379/pike or /opt/pike.yml")
   165  	// 管理后台地址
   166  	rootCmd.Flags().StringVar(&adminAddr, "admin", "", "The address of admin web page, e.g.: :9013")
   167  	// 告警发送地址
   168  	rootCmd.Flags().StringVar(&alarmURL, "alarm", "", "The alarm request url, alarm will post to the url, e.g.: http://192.168.1.2:3000/alarms")
   169  	// 日志文件
   170  	rootCmd.Flags().StringVar(&logOutputPath, "log", "", "The log path, e.g.: /var/pike.log or lumberjack:///tmp/pike.log?maxSize=100&maxAge=1&compress=true")
   171  
   172  	return rootCmd.Execute()
   173  }
   174  
   175  func run() {
   176  	logger := log.Default()
   177  
   178  	go config.Watch(func() {
   179  		err := update()
   180  		if err != nil {
   181  			logger.Error("update config fail",
   182  				zap.Error(err),
   183  			)
   184  			go doAlarm("config", err.Error())
   185  		} else {
   186  			logger.Info("update config success")
   187  		}
   188  	})
   189  
   190  	err := update()
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  }
   195  
   196  func isHelpCmd() bool {
   197  	for _, arg := range os.Args {
   198  		if arg == "-h" || arg == "--help" {
   199  			return true
   200  		}
   201  	}
   202  	return false
   203  }
   204  
   205  // isDev 判断是否开发环境
   206  func isDev() bool {
   207  	return os.Getenv("GO_ENV") == "dev"
   208  }
   209  
   210  func main() {
   211  	defer config.Close()
   212  	defer store.Close()
   213  
   214  	log.Default().Info("pike is running")
   215  	c := make(chan os.Signal, 1)
   216  	signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
   217  	for si := range c {
   218  		log.Default().Info("closing",
   219  			zap.String("signal", si.String()),
   220  		)
   221  		// 如果非开发环境,则需要close所有的server
   222  		if !isDev() {
   223  			server.Close()
   224  		}
   225  		os.Exit(0)
   226  	}
   227  }