123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- package infoc
- import (
- "bytes"
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "strconv"
- "strings"
- "sync"
- "time"
- "go-common/library/log"
- "go-common/library/net/metadata"
- "go-common/library/net/netutil"
- xtime "go-common/library/time"
- )
- const (
- _infocSpliter = "\001"
- _infocReplacer = "|"
- _infocLenStart = 2
- _infocLenEnd = 6
- _protocolLen = 6
- _infocTimeout = 50 * time.Millisecond
- )
- var (
- _infocMagic = []byte{172, 190} // NOTE: magic 0xAC0xBE
- _infocHeaderLen = []byte{0, 0, 0, 0} // NOTE: body len placeholder
- _infocType = []byte{0, 0} // NOTE: type 0
- _maxRetry = 10
- // ErrFull error chan buffer full.
- ErrFull = errors.New("infoc: chan buffer full")
- )
- var (
- // ClientWeb ...
- ClientWeb = "web"
- ClientIphone = "iphone"
- ClientIpad = "ipad"
- ClientAndroid = "android"
- ItemTypeAv = "av"
- ItemTypeBangumi = "bangumi"
- ItemTypeLive = "live"
- ItemTypeTopic = "topic"
- ItemTypeRank = "rank"
- ItemTypeActivity = "activity"
- ItemTypeTag = "tag"
- ItemTypeAD = "ad"
- ItemTypeLV = "lv"
- ActionClick = "click"
- ActionPlay = "play"
- ActionFav = "fav"
- ActionCoin = "coin"
- ActionDM = "dm"
- ActionToView = "toview"
- ActionShare = "share"
- ActionSpace = "space"
- Actionfollow = "follow"
- ActionHeartbeat = "heartbeat"
- ActionAnswer = "answer"
- )
- // Config is infoc config.
- type Config struct {
- TaskID string
- // udp or tcp
- Proto string
- Addr string
- ChanSize int
- DialTimeout xtime.Duration
- WriteTimeout xtime.Duration
- }
- // Infoc infoc struct.
- type Infoc struct {
- c *Config
- header []byte
- msgs chan *bytes.Buffer
- dialTimeout time.Duration
- writeTimeout time.Duration
- pool sync.Pool
- waiter sync.WaitGroup
- }
- // New new infoc logger.
- func New(c *Config) (i *Infoc) {
- i = &Infoc{
- c: c,
- header: []byte(c.TaskID),
- msgs: make(chan *bytes.Buffer, c.ChanSize),
- dialTimeout: time.Duration(c.DialTimeout),
- writeTimeout: time.Duration(c.WriteTimeout),
- pool: sync.Pool{
- New: func() interface{} {
- return &bytes.Buffer{}
- },
- },
- }
- if i.dialTimeout == 0 {
- i.dialTimeout = _infocTimeout
- }
- if i.writeTimeout == 0 {
- i.writeTimeout = _infocTimeout
- }
- i.waiter.Add(1)
- go i.writeproc()
- return
- }
- func (i *Infoc) buf() *bytes.Buffer {
- return i.pool.Get().(*bytes.Buffer)
- }
- func (i *Infoc) freeBuf(buf *bytes.Buffer) {
- buf.Reset()
- i.pool.Put(buf)
- }
- // Info record log to file.
- func (i *Infoc) Info(args ...interface{}) (err error) {
- err, res := i.info(args...)
- if err != nil {
- return
- }
- select {
- case i.msgs <- res:
- default:
- i.freeBuf(res)
- err = ErrFull
- }
- return
- }
- // Infov support filter mirror request
- func (i *Infoc) Infov(ctx context.Context, args ...interface{}) (err error) {
- if metadata.Bool(ctx, metadata.Mirror) {
- return
- }
- return i.Info(args...)
- }
- func getValue(i interface{}) (s string) {
- switch v := i.(type) {
- case int:
- s = strconv.FormatInt(int64(v), 10)
- case int64:
- s = strconv.FormatInt(v, 10)
- case string:
- s = v
- case bool:
- s = strconv.FormatBool(v)
- default:
- s = fmt.Sprint(i)
- }
- return
- }
- // Close close the connection.
- func (i *Infoc) Close() error {
- i.msgs <- nil
- i.waiter.Wait()
- return nil
- }
- // writeproc write data into connection.
- func (i *Infoc) writeproc() {
- var (
- msg *bytes.Buffer
- conn net.Conn
- err error
- )
- bc := netutil.BackoffConfig{
- MaxDelay: 15 * time.Second,
- BaseDelay: 1.0 * time.Second,
- Factor: 1.6,
- Jitter: 0.2,
- }
- for {
- if msg = <-i.msgs; msg == nil {
- break // quit infoc writeproc
- }
- var j int
- for j = 0; j < _maxRetry; j++ {
- if conn == nil || err != nil {
- if conn, err = net.DialTimeout(i.c.Proto, i.c.Addr, i.dialTimeout); err != nil {
- log.Error("infoc net dial error(%v)", err)
- time.Sleep(bc.Backoff(j))
- continue
- }
- }
- if i.writeTimeout != 0 {
- conn.SetWriteDeadline(time.Now().Add(i.writeTimeout))
- }
- if _, err = conn.Write(msg.Bytes()); err != nil {
- log.Error("infoc conn write error(%v)", err)
- conn.Close()
- time.Sleep(bc.Backoff(j))
- continue
- }
- break
- }
- if j == _maxRetry {
- log.Error("infoc reached max retry times")
- }
- i.freeBuf(msg)
- }
- i.waiter.Done()
- if conn != nil && err == nil {
- conn.Close()
- }
- }
- func (i *Infoc) info(args ...interface{}) (err error, buf *bytes.Buffer) {
- if len(args) == 0 {
- return nil, nil
- }
- res := i.buf()
- res.Write(_infocMagic) // type and body buf, for calc length.
- res.Write(_infocHeaderLen) // placeholder
- res.Write(_infocType)
- res.Write(i.header)
- res.WriteString(strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10))
- // // append first arg
- _, err = res.WriteString(getValue(args[0]))
- for _, arg := range args[1:] {
- // append ,arg
- res.WriteString(_infocSpliter)
- _, err = res.WriteString(strings.Replace(getValue(arg), _infocSpliter, _infocReplacer, -1))
- }
- if err != nil {
- i.freeBuf(res)
- return
- }
- bs := res.Bytes()
- binary.BigEndian.PutUint32(bs[_infocLenStart:_infocLenEnd], uint32(res.Len()-_protocolLen))
- buf = res
- return
- }
|