github.com/unirita/cuto@v0.9.8-0.20160830082821-aa6652f877b7/servant/remote/session.go (about) 1 // マスタからの接続要求を受け取る。 2 // 作成者:2015/04/09 本田 3 // copyright. unirita Inc. 4 5 package remote 6 7 import ( 8 "net" 9 "time" 10 11 "github.com/unirita/cuto/console" 12 "github.com/unirita/cuto/db" 13 "github.com/unirita/cuto/log" 14 "github.com/unirita/cuto/message" 15 "github.com/unirita/cuto/servant/config" 16 "github.com/unirita/cuto/servant/job" 17 ) 18 19 type endSig struct{} 20 21 // masterからのジョブ実行要求1つに対応する構造体。 22 type Session struct { 23 Conn net.Conn 24 Body string 25 26 endHeartbeatCh chan endSig 27 doJobRequest func(req *message.Request, conf *config.ServantConfig, stCh chan<- string) *message.Response 28 } 29 30 // Sessionオブジェクトのコンストラクタ 31 func NewSession(conn net.Conn, body string) *Session { 32 s := new(Session) 33 s.Conn = conn 34 s.Body = body 35 s.doJobRequest = job.DoJobRequest 36 s.startHeartbeat() 37 return s 38 } 39 40 // セッションに対応した処理を実行する。 41 // 引数:conf 設定情報 42 // 戻り値:なし 43 func (s *Session) Do(conf *config.ServantConfig) error { 44 defer s.Conn.Close() 45 defer s.endHeartbeat() 46 47 var msg string 48 req := new(message.Request) 49 if err := req.ParseJSON(s.Body); err != nil { 50 chk := new(message.JobCheck) 51 if err := chk.ParseJSON(s.Body); err != nil { 52 console.Display("CTS015E", err.Error()) 53 return err 54 } 55 56 resultMsg, err := s.doJobCheck(chk, conf) 57 if err != nil { 58 log.Error(err) 59 return err 60 } 61 62 msg = resultMsg 63 } else { 64 resMsg, err := s.doRequest(req, conf) 65 if err != nil { 66 log.Error(err) 67 return err 68 } 69 msg = resMsg 70 } 71 72 if _, err := s.Conn.Write([]byte(msg + MsgEnd)); err != nil { 73 log.Error(err) 74 return err 75 } 76 77 return nil 78 } 79 80 func (s *Session) doRequest(req *message.Request, conf *config.ServantConfig) (string, error) { 81 err := req.ExpandServantVars() 82 if err != nil { 83 console.Display("CTS015E", err.Error()) 84 res := s.createErrorResponse(req, err) 85 return res.GenerateJSON() 86 } 87 88 stCh := make(chan string, 1) 89 go s.waitAndSendStartTime(stCh) 90 defer close(stCh) 91 92 res := s.doJobRequest(req, conf, stCh) 93 return res.GenerateJSON() 94 } 95 96 func (s *Session) doJobCheck(chk *message.JobCheck, conf *config.ServantConfig) (string, error) { 97 result := job.DoJobResultCheck(chk, conf) 98 return result.GenerateJSON() 99 } 100 101 // ハートビートを開始する。 102 func (s *Session) startHeartbeat() { 103 s.endHeartbeatCh = make(chan endSig, 1) 104 go func() { 105 t := time.Duration(config.Servant.Job.HeartbeatSpanSec) * time.Second 106 for { 107 select { 108 case <-s.endHeartbeatCh: 109 return 110 case <-time.After(t): 111 log.Debug("send heatbeat...") 112 s.Conn.Write([]byte(message.HEARTBEAT + MsgEnd)) 113 } 114 } 115 }() 116 } 117 118 // ハートビートメッセージを停止する 119 func (s *Session) endHeartbeat() { 120 s.endHeartbeatCh <- endSig{} 121 close(s.endHeartbeatCh) 122 } 123 124 // スタート時刻の決定を待ち、masterへ送信する。 125 func (s *Session) waitAndSendStartTime(stCh <-chan string) { 126 st := <-stCh 127 if len(st) == 0 { 128 // (主にチャネルがクローズされることにより)空文字列が送られてきた場合は何もしない。 129 return 130 } 131 132 s.Conn.Write([]byte(message.ST_HEADER + st + MsgEnd)) 133 } 134 135 // ジョブ実行結果が得られないようなエラーが発生した場合のレスポンスメッセージを生成する。 136 func (s *Session) createErrorResponse(req *message.Request, err error) *message.Response { 137 res := new(message.Response) 138 res.NID = req.NID 139 res.JID = req.JID 140 res.Stat = db.ABNORMAL 141 res.Detail = err.Error() 142 return res 143 }