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)
		}
	}
}
Beispiel #2
0
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
}
Beispiel #5
0
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
}
Beispiel #6
0
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
}
Beispiel #7
0
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
}
Beispiel #9
0
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
}
Beispiel #10
0
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)
	}
}
Beispiel #11
0
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)
	}
}
Beispiel #12
0
// 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
}
Beispiel #13
0
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)
		}
	}
}
Beispiel #14
0
// 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
}
Beispiel #15
0
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)
		}
	}
}
Beispiel #16
0
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.")
	}
}
Beispiel #17
0
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
}
Beispiel #18
0
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
}
Beispiel #19
0
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)
}
Beispiel #20
0
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)
}
Beispiel #21
0
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)
	}
}
Beispiel #22
0
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
}
Beispiel #23
0
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
}
Beispiel #24
0
// 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)
	}
}
Beispiel #25
0
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)
}
Beispiel #26
0
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)
}