gitee.com/woood2/luca@v1.0.4/cmd/cron/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"gitee.com/woood2/luca/cmd/cron/internal/handler"
     7  	_ "gitee.com/woood2/luca/cmd/cron/internal/handler"
     8  	"gitee.com/woood2/luca/cmd/cron/internal/job"
     9  	lucaSDK "gitee.com/woood2/luca/cmd/micro/pkg"
    10  	"gitee.com/woood2/luca/internal/cache"
    11  	"gitee.com/woood2/luca/internal/conf"
    12  	"gitee.com/woood2/luca/internal/discovery"
    13  	myLog "gitee.com/woood2/luca/internal/log"
    14  	"gitee.com/woood2/luca/internal/status"
    15  	"gitee.com/woood2/luca/internal/trace"
    16  	consulsd "github.com/go-kit/kit/sd/consul"
    17  	"github.com/robfig/cron/v3"
    18  	"go.uber.org/zap"
    19  	"log"
    20  	"os"
    21  	"os/signal"
    22  	"runtime"
    23  	"syscall"
    24  	"time"
    25  )
    26  
    27  const (
    28  	entrance               = "cron"
    29  	electionExpire         = 30 * time.Second
    30  	campaignRetryFrequency = 5 * time.Second
    31  	renewFrequency         = 20 * time.Second
    32  )
    33  
    34  var electionKey string
    35  
    36  func main() {
    37  	//load config
    38  	attr := conf.Load("application.yml", "configs/application.yml")
    39  	//consul
    40  	client, consulClient := discovery.Client(attr.Consul.Host, attr.Consul.Port)
    41  	conf.MergeConsul(attr, consulClient)
    42  	//election
    43  	electionKey = attr.Project + "-" + entrance + "-election"
    44  	//zap logger
    45  	logger := myLog.Build(attr.Env, attr.Project, entrance, attr.Host, attr.ConsoleLog)
    46  	defer logger.Sync()
    47  	//redis
    48  	redisCache := cache.NewRedis(attr.Redis)
    49  	cache.SetRedis(redisCache)
    50  	//zipkin
    51  	trace.Open(attr.Zipkin)
    52  	defer trace.Close()
    53  	//sdk
    54  	setupSDK(client, logger)
    55  	//Pprof
    56  	go status.Pprof(attr.Pprof, attr.Env, attr.Cron.PprofAddr)
    57  	//hystrix
    58  	go status.Hystrix(attr.Cron.HystrixPort)
    59  	//cron init
    60  	c := cron.New(cron.WithSeconds())
    61  	handler.RegisterHandlers(attr.Project, entrance, logger)
    62  	jobs := job.List()
    63  	for _, job := range jobs {
    64  		c.AddJob(job.Spec, cron.NewChain(myRecover(logger)).Then(job))
    65  	}
    66  	//campaign & start
    67  	go func() {
    68  		if err := campaign(); err != nil {
    69  			log.Panicf("campaigning err: %v\n", err)
    70  		}
    71  		log.Println("campaign success, cron start")
    72  		c.Start()
    73  		if err := renew(); err != nil {
    74  			log.Panicf("renewing err: %v\n", err)
    75  		}
    76  	}()
    77  	//graceful shutdown
    78  	quit := make(chan os.Signal)
    79  	signal.Notify(quit, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)
    80  	<-quit
    81  	ctx := c.Stop()
    82  	t := time.NewTimer(5 * time.Second)
    83  	select {
    84  	case <-t.C:
    85  		log.Println("Shutting down server in 5 sec...")
    86  	case <-ctx.Done():
    87  		log.Println("Shutting down server...")
    88  	}
    89  }
    90  
    91  //return if success
    92  //keep retry if fail
    93  func campaign() error {
    94  	rds := cache.Redis().Client
    95  	for {
    96  		resp := rds.SetNX(context.Background(), electionKey, 1, electionExpire)
    97  		succ, err := resp.Result()
    98  		if err != nil || !succ {
    99  			time.Sleep(campaignRetryFrequency)
   100  			continue
   101  		}
   102  		return nil
   103  	}
   104  }
   105  
   106  //keep delay expire
   107  func renew() error {
   108  	rds := cache.Redis().Client
   109  	t := time.NewTicker(renewFrequency)
   110  	for {
   111  		<-t.C
   112  		resp := rds.Expire(context.Background(), electionKey, electionExpire)
   113  		succ, err := resp.Result()
   114  		if err != nil || !succ {
   115  			return err
   116  		}
   117  	}
   118  }
   119  
   120  func myRecover(logger *zap.Logger) cron.JobWrapper {
   121  	return func(j cron.Job) cron.Job {
   122  		return cron.FuncJob(func() {
   123  			defer func() {
   124  				if r := recover(); r != nil {
   125  					const size = 64 << 10
   126  					buf := make([]byte, size)
   127  					buf = buf[:runtime.Stack(buf, false)]
   128  					err, ok := r.(error)
   129  					if !ok {
   130  						err = fmt.Errorf("%v", r)
   131  					}
   132  					logger.Error("recover from panic",
   133  						zap.Any("err", err),
   134  						zap.String("stack", string(buf)),
   135  					)
   136  				}
   137  			}()
   138  			j.Run()
   139  		})
   140  	}
   141  }
   142  
   143  func setupSDK(client consulsd.Client, zapLogger *zap.Logger) {
   144  	lucaClient := lucaSDK.NewSD(client, "weibo2", "todo", zapLogger, trace.ClientTrace())
   145  	lucaSDK.SetGlobal(lucaClient)
   146  	//More...
   147  }