コード例 #1
0
ファイル: session_proxy.go プロジェクト: wfxiang08/rpc_proxy
//
//
// 等待Request请求的返回: Session最终被Block住
//
func (s *Session) handleResponse(r *Request) {
	// 等待结果的出现
	r.Wait.Wait()

	// 将Err转换成为Exception
	if r.Response.Err != nil {
		r.Response.Data = GetThriftException(r, "proxy_session")
		log.Infof(Magenta("---->Convert Error Back to Exception, Err: %v"), r.Response.Err)
	}

	// 如何处理Data和Err呢?
	incrOpStats(r.Request.Name, microseconds()-r.Start)
}
コード例 #2
0
ファイル: session_proxy.go プロジェクト: wfxiang08/rpc_proxy
func NewSessionSize(c thrift.TTransport, address string, verbose bool,
	bufsize int, timeout int) *Session {

	s := &Session{
		CreateUnix:               time.Now().Unix(),
		RemoteAddress:            address,
		verbose:                  verbose,
		TBufferedFramedTransport: NewTBufferedFramedTransport(c, time.Microsecond*100, 20),
	}

	// Reader 处理Client发送过来的消息
	// Writer 将后端服务的数据返回给Client
	log.Infof(Green("NewSession To: %s"), s.RemoteAddress)
	return s
}
コード例 #3
0
ファイル: utils_log.go プロジェクト: wfxiang08/rpc_proxy
//
// 设置LogLevel
//
func SetLogLevel(level string) {
	var lv = log.LEVEL_INFO
	switch strings.ToLower(level) {
	case "error":
		lv = log.LEVEL_ERROR
	case "warn", "warning":
		lv = log.LEVEL_WARN
	case "debug":
		lv = log.LEVEL_DEBUG
	case "info":
		fallthrough
	default:
		lv = log.LEVEL_INFO
	}
	log.SetLevel(lv)
	log.Infof("set log level to %s", lv)
}
コード例 #4
0
func NewBackendConn(addr string, delegate *BackService, service string, verbose bool) *BackendConn {
	requestMap, _ := NewRequestMap(4096)

	var minSeqId int32
	backendConnIndexMutex.Lock()
	log.Infof("Create Backend Conn with index: %d", backendConnIndex)

	// 主要是区分不同的: backendConnIndex
	minSeqId = backendConnIndex

	backendConnIndex += 1
	if backendConnIndex > 100 {
		backendConnIndex = 0
	}
	backendConnIndexMutex.Unlock()

	// BACKEND_CONN_MAX_SEQ_ID = 1000000
	minSeqId = minSeqId * BACKEND_CONN_MAX_SEQ_ID

	bc := &BackendConn{
		addr:             addr,
		service:          service,
		input:            make(chan *Request, 1024),
		seqNumRequestMap: requestMap,

		currentSeqId: minSeqId,
		minSeqId:     minSeqId,
		maxSeqId:     minSeqId + BACKEND_CONN_MAX_SEQ_ID - 1,
		Index:        INVALID_ARRAY_INDEX,

		delegate: delegate,
		verbose:  verbose,
	}
	go bc.Run()
	return bc
}
コード例 #5
0
func RpcMain(binaryName string, serviceDesc string, configCheck ConfigCheck,
	serverFactory ServerFactorory, buildDate string, gitVersion string) {

	// 1. 准备解析参数
	usage = fmt.Sprintf(usage, binaryName, binaryName)

	version := fmt.Sprintf("Version: %s\nBuildDate: %s\nDesc: %s\nAuthor: [email protected]", gitVersion, buildDate, serviceDesc)
	args, err := docopt.Parse(usage, nil, true, version, true)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if s, ok := args["-V"].(bool); ok && s {
		fmt.Println(Green(version))
		os.Exit(1)
	}

	// 这就是为什么 Codis 傻乎乎起一个 http server的目的
	if s, ok := args["--profile-addr"].(string); ok && len(s) > 0 {
		go func() {
			log.Printf(Red("Profile Address: %s"), s)
			log.Println(http.ListenAndServe(s, nil))
		}()
	}

	// 2. 解析Log相关的配置
	log.SetLevel(log.LEVEL_INFO)

	var maxKeepDays int = 3
	if s, ok := args["--log-keep-days"].(string); ok && s != "" {
		v, err := strconv.ParseInt(s, 10, 32)
		if err != nil {
			log.PanicErrorf(err, "invalid max log file keep days = %s", s)
		}
		maxKeepDays = int(v)
	}

	// set output log file
	if s, ok := args["-L"].(string); ok && s != "" {
		f, err := log.NewRollingFile(s, maxKeepDays)
		if err != nil {
			log.PanicErrorf(err, "open rolling log file failed: %s", s)
		} else {
			defer f.Close()
			log.StdLog = log.New(f, "")
		}
	}
	log.SetLevel(log.LEVEL_INFO)
	log.SetFlags(log.Flags() | log.Lshortfile)

	// set log level
	if s, ok := args["--log-level"].(string); ok && s != "" {
		SetLogLevel(s)
	}

	// 没有就没有
	workDir, _ := args["--work-dir"].(string)
	codeUrlVersion, _ := args["--code-url-version"].(string)
	if len(workDir) == 0 {
		workDir, _ = os.Getwd()
	}

	log.Printf("WorkDir: %s, CodeUrl: %s, Wd: %s", workDir, codeUrlVersion)

	// 3. 解析Config
	configFile := args["-c"].(string)
	conf, err := LoadConf(configFile)
	if err != nil {
		log.PanicErrorf(err, "load config failed")
	}

	// 额外的配置信息
	conf.WorkDir = workDir
	conf.CodeUrlVersion = codeUrlVersion

	if configCheck != nil {
		configCheck(conf)
	} else {
		log.Panic("No Config Check Given")
	}
	// 每次启动的时候都打印版本信息
	log.Infof(Green("-----------------\n%s\n--------------------------------------------------------------------"), version)

	// 启动服务
	server := serverFactory(conf)
	server.Run()
}
コード例 #6
0
func (s *NonBlockSession) Serve(d Dispatcher, maxPipeline int) {
	var errlist errors.ErrorList

	defer func() {
		// 只限制第一个Error
		if err := errlist.First(); err != nil {
			log.Infof("Session [%p] closed, Error = %v", s, err)
		} else {
			log.Infof("Session [%p] closed, Quit", s)
		}
	}()

	// 来自connection的各种请求
	tasks := make(chan *Request, maxPipeline)
	go func() {
		defer func() {
			// 出现错误了,直接关闭Session
			s.Close()

			log.Infof(Red("Session [%p] closed, Abandon %d Tasks"), s, len(tasks))

			for _ = range tasks { // close(tasks)关闭for loop

			}
		}()
		if err := s.loopWriter(tasks); err != nil {
			errlist.PushBack(err)
		}
	}()

	// 用于等待for中的go func执行完毕
	var wait sync.WaitGroup
	for true {
		// Reader不停地解码, 将Request
		request, err := s.ReadFrame()

		if err != nil {
			errlist.PushBack(err)
			break
		}
		// 来自proxy的请求, request中不带有service
		r, err1 := NewRequest(request, false)
		if err1 != nil {
			errlist.PushBack(err1)
			break
		}

		wait.Add(1)
		go func() {
			// 异步执行(直接通过goroutine来调度,因为: SessionNonBlock中的不同的Request相互独立)

			_ = s.handleRequest(r, d)
			//			if r.Request.TypeId != MESSAGE_TYPE_HEART_BEAT {
			//				log.Debugf(Magenta("[%p] --> SeqId: %d, Goroutine: %d"), s, r.Request.SeqId, runtime.NumGoroutine())
			//			}

			// 数据请求完毕之后,将Request交给tasks, 然后再写回Client

			tasks <- r
			wait.Done()
			//			if r.Request.TypeId != MESSAGE_TYPE_HEART_BEAT {
			//				log.Printf("Time RT: %.3fms", float64(microseconds()-r.Start)*0.001)
			//			}

		}()
	}
	// 等待go func执行完毕
	wait.Wait()
	close(tasks)
	return
}
コード例 #7
0
ファイル: server_proxy.go プロジェクト: wfxiang08/rpc_proxy
//
// 两参数是必须的:  ProductName, zkAddress, frontAddr可以用来测试
//
func (p *ProxyServer) Run() {

	var transport thrift.TServerTransport
	var err error

	log.Printf(Magenta("Start Proxy at Address: %s"), p.proxyAddr)
	// 读取后端服务的配置
	isUnixDomain := false
	if !strings.Contains(p.proxyAddr, ":") {
		if rpc_utils.FileExist(p.proxyAddr) {
			os.Remove(p.proxyAddr)
		}
		transport, err = rpc_utils.NewTServerUnixDomain(p.proxyAddr)
		isUnixDomain = true
	} else {
		transport, err = thrift.NewTServerSocket(p.proxyAddr)
	}
	if err != nil {
		log.ErrorErrorf(err, "Server Socket Create Failed: %v, Front: %s", err, p.proxyAddr)
	}

	// 开始监听
	//	transport.Open()
	transport.Listen()

	ch := make(chan thrift.TTransport, 4096)
	defer close(ch)
	defer func() {
		log.Infof(Red("==> Exit rpc_proxy"))
		if err := recover(); err != nil {
			log.Infof("Error rpc_proxy: %s", err)
		}
	}()
	go func() {
		var address string
		for c := range ch {
			// 为每个Connection建立一个Session
			socket, ok := c.(rpc_utils.SocketAddr)
			if isUnixDomain {
				address = p.proxyAddr
			} else if ok {
				address = socket.Addr().String()
			} else {
				address = "unknow"
			}
			x := NewSession(c, address, p.verbose)
			// Session独立处理自己的请求
			go x.Serve(p.router, 1000)
		}
	}()

	// Accept什么时候出错,出错之后如何处理呢?
	for {
		c, err := transport.Accept()
		if err != nil {
			log.ErrorErrorf(err, "Accept Error: %v", err)
			break
		} else {
			ch <- c
		}
	}
}
コード例 #8
0
ファイル: session_proxy.go プロジェクト: wfxiang08/rpc_proxy
// Session是同步处理请求,因此没有必要搞多个
func (s *Session) Serve(d Dispatcher, maxPipeline int) {
	defer func() {
		s.Close()
		log.Infof(Red("==> Session Over: %s, Total %d Ops"), s.RemoteAddress, s.Ops)
		if err := recover(); err != nil {
			log.Infof(Red("Catch session error: %v"), err)
		}
	}()

	for true {
		// 1. 读取请求
		request, err := s.ReadFrame()
		s.Ops += 1

		// 读取出错,直接退出
		if err != nil {
			err1, ok := err.(thrift.TTransportException)
			if !ok || err1.TypeId() != thrift.END_OF_FILE {
				log.ErrorErrorf(err, Red("ReadFrame Error: %v"), err)
			}
			// r.Recycle()
			return
		}

		var r *Request
		// 2. 处理请求
		r, err = s.handleRequest(request, d)
		if err != nil {
			// r.Recycle() // 出错之后也要主动返回数据
			log.ErrorErrorf(err, Red("handleRequest Error: %v"), err)
			r.Response.Err = err
		}

		// 3. 等待请求处理完毕
		s.handleResponse(r)

		// 4. 将结果写回给Client
		if s.verbose {
			log.Debugf("[%s]Session#loopWriter --> client FrameSize: %d",
				r.Service, len(r.Response.Data))
		}

		// 5. 将请求返回给Client, r.Response.Data ---> Client
		_, err = s.TBufferedFramedTransport.Write(r.Response.Data)
		r.Recycle() // 重置: Request

		if err != nil {
			log.ErrorErrorf(err, "Write back Data Error: %v", err)
			return
		}

		// 6. Flush
		err = s.TBufferedFramedTransport.FlushBuffer(true) // len(tasks) == 0
		if err != nil {
			log.ErrorErrorf(err, "Write back Data Error: %v", err)
			return
		}

		// r.Recycle()
	}
}
コード例 #9
0
ファイル: server_lb.go プロジェクト: wfxiang08/rpc_proxy
func (p *ThriftLoadBalanceServer) Run() {
	//	// 1. 创建到zk的连接

	// 127.0.0.1:5555 --> 127_0_0_1:5555

	exitSignal := make(chan os.Signal, 1)

	signal.Notify(exitSignal, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	// syscall.SIGKILL
	// kill -9 pid
	// kill -s SIGKILL pid 还是留给运维吧
	//

	// 注册服务
	evtExit := make(chan interface{})

	// 初始状态为不上线
	var state atomic2.Bool
	state.Set(false)
	stateChan := make(chan bool)

	serviceEndpoint := RegisterService(p.serviceName, p.frontendAddr, p.lbServiceName,
		p.topo, evtExit, p.config.WorkDir, p.config.CodeUrlVersion, &state, stateChan)

	//	var suideTime time.Time

	//	isAlive := true

	// 3. 读取后端服务的配置
	var transport thrift.TServerTransport
	var err error

	isUnixDomain := false
	// 127.0.0.1:9999(以:区分不同的类型)
	if !strings.Contains(p.frontendAddr, ":") {
		if rpc_utils.FileExist(p.frontendAddr) {
			os.Remove(p.frontendAddr)
		}
		transport, err = rpc_utils.NewTServerUnixDomain(p.frontendAddr)
		isUnixDomain = true
	} else {
		transport, err = thrift.NewTServerSocket(p.frontendAddr)
	}

	if err != nil {
		log.ErrorErrorf(err, "Server Socket Create Failed: %v", err)
		panic(fmt.Sprintf("Invalid FrontendAddress: %s", p.frontendAddr))
	}

	err = transport.Listen()
	if err != nil {
		log.ErrorErrorf(err, "Server Socket Create Failed: %v", err)
		panic(fmt.Sprintf("Binding Error FrontendAddress: %s", p.frontendAddr))
	}

	ch := make(chan thrift.TTransport, 4096)
	defer close(ch)

	// 等待后端服务起来
	waitTicker := time.NewTicker(time.Second)

	// 等待上线采用的策略:
	// 1. 检测到有效的Worker注册之后,再等5s即可像zk注册; 避免了Worker没有连接上来,就有请求过来
	// 2. 一旦注册之后,就不再使用该策略;避免服务故障时,lb频繁更新zk, 导致proxy等频繁读取zk
START_WAIT:
	for true {
		select {
		case <-waitTicker.C:
			if p.backendService.Active() <= 0 {
				log.Infof("Sleep Waiting for back Service to Start")
				time.Sleep(time.Second)
			} else {
				break START_WAIT
			}
		case <-exitSignal:
			// 直接退出
			transport.Interrupt()
			transport.Close()
			return
		}
	}

	log.Infof("Stop Waiting")
	// 停止: waitTicker, 再等等就继续了
	waitTicker.Stop()
	time.Sleep(time.Second * 5)

	log.Infof("Begin to Reg To Zk...")
	state.Set(true)
	stateChan <- true

	// 强制退出? TODO: Graceful退出
	go func() {
		<-exitSignal

		// 通知RegisterService终止循环
		evtExit <- true
		log.Info(Green("Receive Exit Signals...."))
		serviceEndpoint.DeleteServiceEndpoint(p.topo)

		start := time.Now().Unix()
		for true {
			// 如果5s内没有接受到新的请求了,则退出
			now := time.Now().Unix()
			if now-p.lastRequestTime.Get() > 5 {
				log.Printf(Red("[%s]Graceful Exit..."), p.serviceName)
				break
			} else {
				log.Printf(Cyan("[%s]Sleeping %d seconds before Exit...\n"),
					p.serviceName, now-start)
				time.Sleep(time.Second)
			}
		}

		transport.Interrupt()
		transport.Close()
	}()

	go func() {
		var address string
		for c := range ch {
			// 为每个Connection建立一个Session
			socket, ok := c.(rpc_utils.SocketAddr)

			if ok {
				if isUnixDomain {
					address = p.frontendAddr
				} else {
					address = socket.Addr().String()
				}
			} else {
				address = "unknow"
			}
			x := NewNonBlockSession(c, address, p.verbose, &p.lastRequestTime)
			// Session独立处理自己的请求
			go x.Serve(p.backendService, 1000)
		}
	}()

	// Accept什么时候出错,出错之后如何处理呢?
	for {
		c, err := transport.Accept()
		if err != nil {
			close(ch)
			break
		} else {
			ch <- c
		}
	}
}