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  }