func (server *ConnectionServer) startListener(workerManager *coresupport.CoreWorkerManager) { if server.GetStatus() != ServerStatusOn { server.SetStatus(ServerStatusOn) options := (*server).option socket := options.Socket() listener, err := net.Listen("tcp", socket) if err == nil { defer listener.Close() defer (*server).connectionDataManager.Close() for server.isAcceptForConnection() { newConnection, err := listener.Accept() if err != nil { rllogger.Outputf( rllogger.LogTerminate, "Can't create connection to %s error: %s", socket, err) server.stat.SendMsg("lost_connection_count", 1) } else { clientAddr := fmt.Sprintf("connection:%s", newConnection.RemoteAddr()) rllogger.Outputf(rllogger.LogDebug, "new %s", clientAddr) tcpConnection := newConnection.(*net.TCPConn) tcpConnection.SetKeepAlive(true) tcpConnection.SetKeepAlivePeriod( time.Duration(1+options.StatusCheckPeriod) * time.Second) server.stat.SendMsg("connection_count", 1) go server.connectionReadProcessing( newConnection, workerManager, clientAddr) } } } else { rllogger.Outputf(rllogger.LogTerminate, "Can't start server at %s error: %s", socket, err) } } }
func main() { confPath := os.Getenv("CONF") optionSrc := options.JsonOptionSrc{FilePath: confPath} if option, err := optionSrc.Load(true); err == nil { flag.Parse() rllogger.Outputf(rllogger.LogInfo, "Starting with '%s'...\n", confPath) if len(toolType) > 0 { // now run for tooling var toolMethods map[string]toolMethod = make(map[string]toolMethod) toolMethods["jwtcreate"] = cryptosupport.JwtCreate toolMethods["jwtcheck"] = cryptosupport.JwtCheck toolMethods["keyscheck"] = cryptosupport.KeysSimpleCheck if method, exists := toolMethods[toolType]; exists { method(toolData, option) } else { panic(fmt.Sprintf("Unknown tool name requested: '%s'", toolType)) } } else { // run as service corelauncher.Launch(option) } } else { rllogger.Outputf(rllogger.LogTerminate, "Option load failed: %s", err) } }
func (server *ConnectionServer) connectionReadProcessing( connection net.Conn, workerManager *coresupport.CoreWorkerManager, label string) { // defer connection.Close() buffer := bufio.NewReader(connection) wait := true hasAnswerChannel := false connectionData := server.connectionDataManager.NewConnection() // TODO: rllogger.LogDebug rllogger.Outputf(rllogger.LogInfo, "new connection %s", connectionData.Cid) sizeBuffer := (*server).option.BufferSize for wait { // lineData, _, err := buffer.ReadLine() if err == nil { server.stat.SendMsg("income_data_size", len(lineData)) if cmd, err := transport.ParseCommand(&lineData); err == nil { workerManager.Processing(cmd, server.connectionDataManager, connectionData) if !hasAnswerChannel { // create backChannel := make(chan coreprocessing.CoreInstruction, sizeBuffer) // dont like closure, only realy need go connectionWriteProcessing( connection, &backChannel, (*server).connectionDataManager, server.stat, label) workerManager.AppendBackChannel(connectionData, &backChannel) hasAnswerChannel = true } } else { server.stat.SendMsg("bad_command_count", 1) rllogger.Outputf(rllogger.LogWarn, "connection %s bad command: %s", connectionData.Cid, err) } } else { // has error if err == io.EOF { // client break connection workerManager.BrokenConnection(connectionData) rllogger.Outputf(rllogger.LogDebug, "broken connection %s", connectionData.Cid) } wait = false } } server.stat.DelOneMsg("connection_count") // remove from methods if server.connectionDataManager.ClientInGroup(connectionData.Cid, connectionsupport.GroupConnectionServer) { cidMethods := coreprocessing.NewRpcServerManager() cidMethods.Remove(connectionData.Cid) server.stat.DelOneMsg("count_connection_server") } else { server.stat.DelOneMsg("count_connection_client") } workerManager.RemoveBackChannel(connectionData) server.connectionDataManager.RemoveConnection(connectionData.Cid) // TODO: rllogger.LogDebug rllogger.Outputf(rllogger.LogInfo, "out connection %s", connectionData.Cid) }
func (handler *Handler) Execute(ins *CoreInstruction) []*CoreInstruction { var result []*CoreInstruction if method, exists := methods[ins.Type]; exists { outIns := method(handler, ins) (*outIns).Cid = (*ins).Cid // post method postMethod, exists := postMethods[ins.Type] size := 1 var externIns []*CoreInstruction if exists { externIns = postMethod(handler, ins, outIns) size += len(externIns) } result = make([]*CoreInstruction, size) result[0] = outIns if exists { for index, insPtr := range externIns { result[index+1] = insPtr } } } else { // send error outIns := NewCoreInstruction(TypeInstructionProblem) (*outIns).Cid = (*ins).Cid (*outIns).answer = ins.MakeErrAnswer( transport.ErrorCodeInternalProblem, "Not implimented handler for this type!") rllogger.Outputf( rllogger.LogError, "Unknown instruction type '%d' from %s", ins.Type, ins.Cid) result = make([]*CoreInstruction, 1) result[0] = outIns } // copy cid always return result }
func NewStatMsg(code string, value interface{}) *StatMsg { var statValue StatValueType switch val := value.(type) { case int: statValue = StatValueType(val) case int32: statValue = StatValueType(val) case int64: statValue = StatValueType(val) case uint32: statValue = StatValueType(val) case uint64: statValue = StatValueType(val) case uintptr: statValue = StatValueType(val) case float32: statValue = StatValueType(val) case float64: statValue = StatValueType(val) default: rllogger.Outputf( rllogger.LogTerminate, "Statistic not accept this type: %T", value) } msg := StatMsg{code: code, value: statValue} return &msg }
func (stat *RlStatistic) Append(item int, value interface{}) { var statValue StatValueType switch val := value.(type) { case StatObjectMeter: statValue = StatValueType(val.GetSize()) case int: statValue = StatValueType(val) case int32: statValue = StatValueType(val) case int64: statValue = StatValueType(val) case uint32: statValue = StatValueType(val) case uint64: statValue = StatValueType(val) case uintptr: statValue = StatValueType(val) case float32: statValue = StatValueType(val) case float64: statValue = StatValueType(val) default: rllogger.Outputf( rllogger.LogTerminate, "Statistic not accept this type: %T", value) } statValue = statValue / oneInStatStep stat.LockChange() defer stat.UnLockChange() oldValue, exists := (*stat).items[item] if exists { statValue += oldValue } (*stat).items[item] = statValue }
func ProcRecordResult( handler *coreprocessing.Handler, inIns *coreprocessing.CoreInstruction, outIns *coreprocessing.CoreInstruction) []*coreprocessing.CoreInstruction { // var result []*coreprocessing.CoreInstruction if answer, _ := outIns.GetAnswer(); (*answer).Error.Code == 0 { if srcCmd, hasCmd := inIns.GetCommand(); hasCmd { rpcManager := coreprocessing.NewRpcServerManager() taskId := (*srcCmd).Params.Task if targetCidPtr := rpcManager.ResultDirectionDict.Get(taskId); targetCidPtr != nil { // check client group if handler.StateCheker.ClientInGroup(*targetCidPtr, connectionsupport.GroupConnectionWsClient) { cmd := transport.NewCommandWithParams( 0, "result", transport.MethodParams{ Cid: *targetCidPtr, Task: taskId, Json: (*srcCmd).Params.Json}) clientIns := coreprocessing.NewCoreInstruction(coreprocessing.TypeInstructionSetResult) clientIns.SetCommand(cmd) result = make([]*coreprocessing.CoreInstruction, 1) result[0] = clientIns } else { rpcManager.ResultBufferDict.Set(taskId, (*srcCmd).Params.Json) } } else { rllogger.Outputf(rllogger.LogError, "Processing pass for: %s", srcCmd) } } } return result }
func (manager *ConnectionDataManager) NewConnection() *ConnectionData { manager.Lock(true) value := (*manager).index + 1 total := (*manager).total + 1 (*manager).index = value (*manager).total = total prefix := manager.rand.GetShotHexPrefix() var index int if total <= ResourcesGroupSize { index = 1 } else { index = int((total-1)/ResourcesGroupSize) + 1 } if index > MaxClientCount/ResourcesGroupSize { index = MaxClientCount / ResourcesGroupSize rllogger.Outputf(rllogger.LogError, "To muth clients! Over %d!", MaxClientCount) } if (*manager).storage[index-1] == nil { (*manager).storage[index-1] = newConnectionDataStorageCell() } manager.Unlock(true) (*manager).storage[index-1].create(value) connectionData := newConnectionData(prefix, value, index) return connectionData }
func sendCallMethod( msg *CoreMsg, serverBusyAccounting *helpers.ServerBusyAccounting, serverMethods *helpers.ServerMethods) ([]*ServerCmd, error) { // var err error var cmd, servCmd *ServerCmd cid := (*msg).Cid method := (*msg).Method if serverMethods.IsPublic(method) { if freeCid, exists := serverMethods.SearchFree(method, serverBusyAccounting); exists { rand := helpers.NewSystemRandom() task := rand.CreateTaskId() taskInfo := fmt.Sprintf("{\"task\": \"%s\"}", task) cmd = NewServerDataCmd(CmdOk, cid, &taskInfo) execDataInst := execData{ Task: task, Data: string((*msg).Data), Method: method} if data, dumpErr := dumps(execDataInst, false); dumpErr != nil { err = dumpErr } else { servCmd = NewServerDataCmd(CmdCallMethod, freeCid, &data) rllogger.Outputf(rllogger.LogDebug, "method '%s' -> %s", method, freeCid) } } else { cmd = NewServerCmd(CmdWaitFree, cid) } } else { cmd = NewServerCmd(CmdProblem, cid) err = errors.New(fmt.Sprintf("Method '%s' not found", method)) } return []*ServerCmd{cmd, servCmd}, err }
func (dispatcher *AnswerDispatcher) Put(cid string, cmd *protocol.ServerCmd) { dispatcher.LockChange() defer dispatcher.UnLockChange() if channelPtr, exists := dispatcher.channels[cid]; exists { *channelPtr <- *cmd } else { rllogger.Outputf(rllogger.LogError, "Channel for answer to %s is lost!", cid) } }
func (dispatcher *AnswerDispatcher) CloseAll() { dispatcher.LockChange() defer dispatcher.UnLockChange() for cid, channelPtr := range (*dispatcher).channels { rllogger.Outputf(rllogger.LogDebug, "close channel %s", cid) close(*channelPtr) delete((*dispatcher).channels, cid) } }
// check token by client public key func ProcAuth(handler *coreprocessing.Handler, inIns *coreprocessing.CoreInstruction) *coreprocessing.CoreInstruction { // client sends in params: // - json: {"key": "<key name>"} // - data: "<base64 token string, use JWT protocol (random data inside)>" // getting (search on option.KeyDir) client public key by name (in command params) handler.Stat.AddOneMsg("auth_request") changes := connectionsupport.StateChanges{ ChangeType: connectionsupport.StateChangesTypeAuth} var result *coreprocessing.CoreInstruction var resultErr error var errCode int if cmd, exists := inIns.GetCommand(); exists { if authData, err := newAuthData(cmd.Params); err == nil { if err := authData.Check(handler.Option); err != nil { errCode = transport.ErrorCodeMethodAuthFailed resultErr = err } } else { resultErr = err errCode = transport.ErrorCodeMethodParamsFormatWrong } } else { errCode = transport.ErrorCodeCommandFormatWrong resultErr = errors.New("Not found command in instruction.") } var answer *transport.Answer var insType int if errCode > 0 { changes.Auth = false answer = inIns.MakeErrAnswer(errCode, fmt.Sprint(resultErr)) insType = coreprocessing.TypeInstructionProblem rllogger.Outputf(rllogger.LogWarn, "Failed auth from %s with error: %s", inIns.Cid, resultErr) } else { changes.Auth = true handler.Stat.AddOneMsg("auth_successfull") answer = inIns.MakeOkAnswer("{\"auth\":true}") insType = coreprocessing.TypeInstructionOk rllogger.Outputf(rllogger.LogDebug, "Successfull auth from %s", inIns.Cid) } result = coreprocessing.NewCoreInstruction(insType) result.SetAnswer(answer) result.StateChanges = &changes return result }
func upConnection( server *RlServer, toCoreBuffer *chan protocol.CoreMsg, answerDispatcher *AnswerDispatcher, statistic *RlStatistic) { // connections options := server.GetOption() socket := options.Socket() listener, err := net.Listen("tcp", socket) if err != nil { rllogger.Outputf( rllogger.LogTerminate, "Can't start server at %s error: %s", socket, err) } defer listener.Close() for server.IsActive() { newConnection, err := listener.Accept() if err != nil { rllogger.Outputf( rllogger.LogTerminate, "Can't start server at %s error: %s", socket, err) } else { clientAddr := fmt.Sprintf("connection:%s", newConnection.RemoteAddr()) rllogger.Outputf(rllogger.LogInfo, "new %s", clientAddr) statistic.Append(StatConnectionCount, 1) server.ChangeConnectionCount(1) tcpConnection := newConnection.(*net.TCPConn) tcpConnection.SetKeepAlive(true) tcpConnection.SetKeepAlivePeriod( time.Duration(1+options.StatusCheckPeriod) * time.Second) // go connectionHandler( server, toCoreBuffer, newConnection, clientAddr, answerDispatcher, statistic) } } }
// main method for client routing to server methods func ProcRouteRpc(handler *coreprocessing.Handler, inIns *coreprocessing.CoreInstruction) *coreprocessing.CoreInstruction { var answer *transport.Answer var errStr string var answerData string insType := coreprocessing.TypeInstructionSkip errCode := 0 // check methods if cmd, exists := inIns.GetCommand(); exists { rpcManager := coreprocessing.NewRpcServerManager() variants := rpcManager.GetCidVariants((*cmd).Method) if len(variants) > 0 { var freeCid string for _, serverCid := range variants { if !handler.StateCheker.ClientBusy(serverCid) { freeCid = serverCid break } } if len(freeCid) > 0 { data := RpcAnswerData{ Cid: freeCid, Task: handler.TaskIdGenerator.CreateTaskId()} // TODO: to debug rllogger.Outputf(rllogger.LogInfo, "rpc call: '%s()' -> %s", (*cmd).Method, data) if strData, err := json.Marshal(data); err == nil { answerData = string(strData) rpcManager.ResultDirectionDict.Set(data.Task, (*inIns).Cid) } else { errCode = transport.ErrorCodeInternalProblem errStr = fmt.Sprintf("Error dump %T: '%s'", data, err) } } else { errCode = transport.ErrorCodeAllServerBusy errStr = fmt.Sprintf("All server busy for method '%s'.", (*cmd).Method) } } else { errCode = transport.ErrorCodeRemouteMethodNotExists errStr = fmt.Sprintf("Method '%s' unregistred or workers lost.", (*cmd).Method) } } else { errCode = transport.ErrorCodeCommandFormatWrong errStr = "Command is empty." } if errCode > 0 { insType = coreprocessing.TypeInstructionProblem answer = inIns.MakeErrAnswer(errCode, errStr) } else { insType = coreprocessing.TypeInstructionOk answer = inIns.MakeOkAnswer(answerData) } result := coreprocessing.NewCoreInstruction(insType) result.SetAnswer(answer) return result }
func (dispatcher *AnswerDispatcher) checkUsedChannel(cid string, channel *chan protocol.ServerCmd) { (*dispatcher).RLockChange() defer (*dispatcher).RUnLockChange() if usedChannel, exists := dispatcher.channels[cid]; exists { if channel != usedChannel { rllogger.Outputf( rllogger.LogWarn, "New channel %p change used %p for client id %s", channel, usedChannel, cid) } } }
func (src JsonOptionSrc) Load(useLog bool) (*SysOption, error) { content, err := ioutil.ReadFile(src.FilePath) if err != nil { if useLog { rllogger.Outputf(rllogger.LogWarn, "Load: %s", err) } return nil, err } option := SysOption{} err = json.Unmarshal(content, &option) if err != nil { if useLog { rllogger.Outputf(rllogger.LogWarn, "Load: %s", err) } return nil, err } if option.Statistic && option.StatisticCheckTime > 0 { return &option, nil } else { return nil, errors.New("Wrong statistic options.") } }
func registrationPublicMethods( msg *CoreMsg, serverBusyAccounting *helpers.ServerBusyAccounting, serverMethods *helpers.ServerMethods) ([]*ServerCmd, error) { // var err error var cmd *ServerCmd cid := (*msg).Cid data := (*msg).Data if errReg := serverMethods.FillFromMsgData(cid, &data); errReg != nil { rllogger.Outputf( rllogger.LogError, "client %s failed methods registration with error: %s", cid, errReg) err = errReg cmd = NewServerExitCmd() } else { rllogger.Outputf(rllogger.LogInfo, "methods of server %s: %s", cid, data) cmd = NewServerCmd(CmdWaitCommand, cid) } return []*ServerCmd{cmd}, err }
func NewServerCreate(optionSrc options.OptionLoder) *RlServer { option, err := optionSrc.Load(true) if err != nil { rllogger.Outputf(rllogger.LogTerminate, "Option load failed: %s", err) } server := RlServer{ option: *option, changeCountLock: new(sync.RWMutex), CoroutineActiveResource: *(helpers.NewCoroutineActiveResource())} rllogger.Output(rllogger.LogInfo, server.info()) return &server }
func worker( index int, instructionsChannel *chan coreprocessing.CoreInstruction, stopSignalChannel *chan bool, outGroups *[]*outChannelGroup, handler *coreprocessing.Handler) { // rllogger.Outputf(rllogger.LogDebug, "Worker %d started...", index) active := true for active { // wait new instruction or finish select { case <-*stopSignalChannel: { active = false } case instruction := <-*instructionsChannel: { for _, newInstruction := range handler.Execute(&instruction) { cid := (*newInstruction).Cid if resIndex, id, err := connectionsupport.ExtractConnectionDataIndexAndId(cid); err == nil { if !(*outGroups)[resIndex].Send(id, newInstruction) { rllogger.Outputf( rllogger.LogError, "Can't send back instruction for %s worker: %d", cid, index) } } else { rllogger.Outputf( rllogger.LogError, "CID format error! value: %s worker: %d", cid, index) } } } } } handler.Close() rllogger.Outputf(rllogger.LogDebug, "Worker %d completed...", index) }
func conntectionWriteAnswer( connection net.Conn, answerChannel *chan protocol.ServerCmd, thisConnectionInfo *connectionInfo, statistic *RlStatistic) { // async write to connection from answer channel active := true var answer *string // clientLabel := thisConnectionInfo.GetLabel() rllogger.Outputf(rllogger.LogDebug, " open writer to %s", clientLabel) for active { answer = nil serverCmd := <-*answerChannel if answerDataPtr, dumpErr := serverCmd.Dump(); dumpErr != nil { rllogger.Outputf( rllogger.LogError, "Dump answer error: %s from %s", dumpErr, clientLabel) answer = protocol.FastErrorAnswer(dumpErr, protocol.ErrCodeFormatProblem) } else { answer = answerDataPtr } active = thisConnectionInfo.IsActive() if active && answer != nil { writen, err := connection.Write([]byte(*answer)) if err != nil { active = false rllogger.Outputf( rllogger.LogError, "Can't send answer to %s error: %s", clientLabel, err) } else { // if this is last command after channel closing active = !serverCmd.IsEmpty() statistic.Append(StatOutcomeDataSize, writen) } } } rllogger.Outputf(rllogger.LogDebug, " closed writer to %s", clientLabel) }
func (stat *RlStatistic) Finish() { if (*stat).used { // wait wait := 0 for (*stat).IsActive() { time.Sleep(1 * time.Second) wait++ } rllogger.Outputf( rllogger.LogInfo, "statistic finished after %d sec.", wait) } }
func ProcCallServerMethod( handler *coreprocessing.Handler, inIns *coreprocessing.CoreInstruction, outIns *coreprocessing.CoreInstruction) []*coreprocessing.CoreInstruction { // var result []*coreprocessing.CoreInstruction if answer, exists := outIns.GetAnswer(); exists { if (*answer).Error.Code == 0 { if srcCmd, hasCmd := inIns.GetCommand(); hasCmd { rpcData := RpcAnswerData{} // little overhead - parse JSON if loadErr := json.Unmarshal([]byte((*answer).Result), &rpcData); loadErr == nil { srcParams := (*srcCmd).Params srcParams.Task = rpcData.Task // replace cid srcParams.Cid = rpcData.Cid newCmd := transport.NewCommandWithParams(0, (*srcCmd).Method, srcParams) resultIns := coreprocessing.NewCoreInstruction(coreprocessing.TypeInstructionExecute) resultIns.SetCommand(newCmd) result = []*coreprocessing.CoreInstruction{resultIns} } else { rllogger.Outputf( rllogger.LogError, "Answer from ProcRouteRpc has incorrect data format, from: %s", inIns.Cid) } } else { rllogger.Outputf(rllogger.LogError, "Answer lost in ProcRouteRpc! from: %s", inIns.Cid) } } } else { rllogger.Outputf(rllogger.LogError, "Answer lost in ProcRouteRpc! from: %s", inIns.Cid) } return result }
func NewStatistic(option options.SysOption) *Statistic { stat := Statistic{ active: true, activeChangeLock: new(sync.RWMutex), messages: make(chan StatMsg, StatBufferSize), closeChan: make(chan bool, 1), iterTime: option.StatisticCheckTime, calcHandlers: make([]AutoCalcHandler, HandlerCountLimit), items: make(map[string]StatValueType), labels: make(map[string]string), silent: !option.Statistic} if stat.iterTime < 1 { stat.iterTime = DefaultIterTime } go statProcessing(&stat) // save to file support if len(option.StatisticFile) > 0 { statFilePath := option.StatisticFile logFileSaveHandler := func(now time.Time, lines *[]string) { // copy file newFilePath := fmt.Sprintf("%s.%d.tmp", statFilePath, now.Unix()) hasCopy := helpers.CopyFile(newFilePath, statFilePath) == nil if hasCopy { os.Remove(statFilePath) } if newFile, err := os.Create(statFilePath); err == nil { defer newFile.Close() writer := bufio.NewWriter(newFile) for _, line := range *lines { writer.WriteString(line) writer.WriteRune('\n') writer.Flush() } } else { rllogger.Outputf(rllogger.LogWarn, "Statistic file %s not avalible!", statFilePath) } if hasCopy { os.Remove(newFilePath) } } stat.AddStatisticOutHandler(logFileSaveHandler) } return &stat }
// Send command to all client func serviceBroadcasting( server *RlServer, answerDispatcher *AnswerDispatcher) { // option := server.GetOption() cmd := protocol.NewServerCmd(protocol.CmdServerStatus, "") debug := rllogger.UseLogDebug() for server.IsActive() { if answerDispatcher.HasClients() { var startTime time.Time if debug { startTime = time.Now() } answerDispatcher.PutAll(cmd) if debug { rllogger.Outputf( rllogger.LogDebug, "Send status request per %f", float32(time.Since(startTime).Seconds())) } } time.Sleep(time.Duration(option.StatusCheckPeriod) * time.Second) } }
func coreWorker( index int, server *RlServer, toCoreBuffer *chan protocol.CoreMsg, answerDispatcher *AnswerDispatcher, serverBusyAccounting *helpers.ServerBusyAccounting, serverMethods *helpers.ServerMethods, statistic *RlStatistic) { // rllogger.Outputf(rllogger.LogInfo, "Worker %d started.", index) needWorker := true option := server.GetOption() var startTime time.Time for needWorker && server.IsActive() { msg := <-(*toCoreBuffer) rllogger.Outputf(rllogger.LogDebug, "worker %d -> %s", index, msg) if msg.IsEmpty() { needWorker = false // send to out answer clients connections } else { if option.CountWorkerTime { startTime = time.Now() } switch msg.Group { case protocol.GroupClientWeb: { // TODO: msg from web socket } case protocol.GroupClientServer, protocol.GroupClientService: { // rpc server client or application client answers, err := protocol.ProcessingMsg( &msg, serverBusyAccounting, serverMethods) for answIndex := 0; answIndex < len(answers); answIndex++ { answer := answers[answIndex] if answer != nil { if answer.TargetIs(protocol.CmdWaitFree) { statistic.Append(StatBusyCount, 1) rllogger.Outputf( rllogger.LogDebug, "Workers busy, msg: %s", msg) } cid := (*answer).Cid if len(cid) < 1 { cid = msg.Cid } answerDispatcher.Put(cid, answer) } } if err != nil { rllogger.Outputf( rllogger.LogWarn, "Processing error %s at worker %d %s", err, index, msg) } } default: { // epic fail rllogger.Outputf( rllogger.LogTerminate, "Error! Unknown group! Worker: %d msg: %s", index, msg) } } if option.CountWorkerTime { statistic.Append( StatWorkerRunTime, float32(time.Since(startTime).Seconds())) } } } rllogger.Outputf(rllogger.LogInfo, "Worker %d finished.", index) }
func connectionHandler( server *RlServer, toCoreBuffer *chan protocol.CoreMsg, connection net.Conn, label string, dispatcher *AnswerDispatcher, stat *RlStatistic) { // server and service connections defer connection.Close() options := server.GetOption() connectionContext := helpers.ConnectionContext{} buffer := bufio.NewReader(connection) wait := true answerWriterActive := false var answer *string answerBuffer := make(chan protocol.ServerCmd, options.BufferSize) localConnectionInfo := newConnectionInfo(label) for wait { line, _, err := buffer.ReadLine() answer = nil if err != nil { if err == io.EOF { // client break connection localConnectionInfo.SetActive(false) if cid, exists := connectionContext.GetCid(); exists { dispatcher.RemoveChannel(cid, true) } } wait = false } else { stat.Append(StatIncomeDataSize, len(line)) cmd, err := protocol.NewClientCmd(&line) if err != nil { wait = false rllogger.Outputf( rllogger.LogWarn, "Incorrect command from %s error: %s", label, err) answer = protocol.FastErrorAnswer(err, protocol.ErrCodeFormatProblem) } else { if cmd.RequiredClose() { wait = false rllogger.Outputf(rllogger.LogInfo, "close command from %s", label) } else { // check auth if cmd.RequiredAuth() && !connectionContext.IsAuth() { answer = protocol.FastErrorAnswer( "Access denied!", protocol.ErrCodeAccessDenied) } else { // try get fast handler if exists, handler := cmd.GetFastHandler(); exists { if newAnswer, err := handler(cmd, &connectionContext, options); err != nil { rllogger.Outputf( rllogger.LogWarn, "Client: %s target: %d Handler error: %s", label, cmd.Target, err) answer = protocol.FastErrorAnswer(err, protocol.ErrCodeProtocolProblem) } else { if dataPtr, dumpErr := newAnswer.Dump(); dumpErr != nil { answer = protocol.FastErrorAnswer(err, protocol.ErrCodeImplementationProblem) } else { answer = dataPtr if exists, contextUpdater := newAnswer.GetContextUpdater(); exists { contextUpdater.Update(&connectionContext) rllogger.Outputf( rllogger.LogDebug, "-> %s context updated: %s", label, connectionContext) } } } } else { clientGroup := connectionContext.GetClientGroup() msg, convertError := protocol.NewCoreMsg(clientGroup, cmd) if convertError != nil { rllogger.Outputf( rllogger.LogWarn, "Msg create error: %s from %s", convertError, label) answer = protocol.FastErrorAnswer( convertError, protocol.ErrCodeProtocolProblem) } else { *toCoreBuffer <- *msg if !answerWriterActive { answerWriterActive = true // this channel to dispatcher dispatcher.AddChannel((*msg).Cid, &answerBuffer) // run async answer writer go conntectionWriteAnswer( connection, &answerBuffer, localConnectionInfo, stat) } } } } } } // write sync handler answer if answer != nil { writen, err := connection.Write([]byte(*answer)) if err != nil { rllogger.Outputf( rllogger.LogError, "Can't send answer to %s error: %s", label, err) } else { stat.Append(StatOutcomeDataSize, writen) } } } } //not need "close(answerBuffer)" dispatcher do it stat.Append(StatConnectionCount, -1) server.ChangeConnectionCount(-1) }
// answer worker func connectionWriteProcessing( connection net.Conn, backChannel *chan coreprocessing.CoreInstruction, dataManager *connectionsupport.ConnectionDataManager, stat statistic.StatisticUpdater, label string) { // wait := true msgSize := 0 var newInstruction coreprocessing.CoreInstruction var statGroupName string for wait { newInstruction = <-*backChannel if newInstruction.IsEmpty() { rllogger.Outputf(rllogger.LogWarn, "Closed write process for %s empty instruction!", label) wait = false } else { // write msgSize = 0 if answer, exists := newInstruction.GetAnswer(); exists { data := answer.DataDump() if data != nil { line := append(*data, byte('\n')) if writen, err := connection.Write(line); err == nil { msgSize += writen } else { rllogger.Outputf(rllogger.LogWarn, "Can't wite answer to %s: %s", label, err) stat.AddOneMsg("lost_connection_count") // wtf ? wait = false } } } // and / or command for client if answerCmd, exists := newInstruction.GetCommand(); exists && wait { data := answerCmd.DataDump() if data != nil { line := append(*data, byte('\n')) if writen, err := connection.Write(line); err == nil { msgSize += writen } else { rllogger.Outputf(rllogger.LogWarn, "Can't wite answer to %s: %s", label, err) stat.AddOneMsg("lost_connection_count") // wtf ? wait = false } } } if msgSize > 0 { stat.SendMsg("outcome_data_size", msgSize) // update state data if newInstruction.StateChanges != nil { // so simple.. without some visitor for statistic if newInstruction.StateChanges.ChangeType == connectionsupport.StateChangesTypeGroup { switch newInstruction.StateChanges.ConnectionClientGroup { case connectionsupport.GroupConnectionClient: statGroupName = "count_connection_client" case connectionsupport.GroupConnectionServer: statGroupName = "count_connection_server" case connectionsupport.GroupConnectionWsClient: statGroupName = "count_connection_web" } stat.AddOneMsg(statGroupName) } // update data in manager dataManager.UpdateState(newInstruction.Cid, newInstruction.StateChanges) } } else { rllogger.Outputf(rllogger.LogWarn, "Empty answer to %s?", label) } if newInstruction.NeedExit() { wait = false rllogger.Outputf(rllogger.LogDebug, "Closed write process for %s from instruction.", label) err := connection.Close() if err != nil { // wait = true ?? rllogger.Outputf(rllogger.LogError, "Close connection %s problem: %s", label, err) } } } } stat.DelOneMsg(statGroupName) close(*backChannel) }