精华内容
下载资源
问答
  • 红火龙果笔记 关于Red Pitaya开源工具的说明
  • 安装pitaya

    2020-08-26 20:51:56
    安装github上安装:https://github.com/topfreegames/pitaya/tree/26e1c39ec7d99bad80c3bd23133400427d4729f6 安装docker:https://www.cnblogs.com/kingsonfu/p/11576797.html 安装docker-compose 解决docker-...
    展开全文
  • Pitaya_airdrop-源码

    2021-03-21 18:24:13
    Pitaya Finance(“ Pitaya”)是非托管,跨链兼容的增量一资产协议。 Pitaya的DeFi长期愿景是提高包容性,并使投资资产(数字和传统资产)的民主化。巨大的价值在于投资者能够轻松,快速地进行投资,节省费用并以...
  • Go开源游戏服务器框架——Pitaya

    千次阅读 2020-09-23 23:54:47
    简介 Pitaya是一款易于使用,快速且轻量级的开源分布式游戏服务器框架,使用golang编写。 源码分析 PlayerConn Acceptor Acceptorwrapper Agent HandleService

    Go开源游戏服务器框架——Pitaya

    简介

    Pitaya是一款由国外游戏公司topfreegames使用golang进行编写,易于使用,快速且轻量级的开源分布式游戏服务器框架
    Pitaya使用etcd作为默认的服务发现组件,提供使用nats和grpc进行远程调用(server to server)的可选配置,并提供在docker中运行以上组件(etcd、nats)的docker-compose配置

    抽象分析

    • PlayerConn
      PlayerConn是一个封装的连接对象,继承net.Conn,并提供一个获取下一个数据包的方法
    	type PlayerConn interface {
    		GetNextMessage() (b []byte, err error)
    		net.Conn
    	}
    
    • Acceptor
      Acceptor代表一个服务端端口进程,接收客户端连接,并用一个内部Chan来维护这些连接对象
    	type Acceptor interface {
    		ListenAndServe()
    		Stop()
    		GetAddr() string
    		GetConnChan() chan PlayerConn
    	}
    
    • Acceptorwrapper
      Acceptorwrapper义如其名就是Acceptor的包装器,因为Acceptor的通过Chan来保存连接
      所以wrapper可以通过遍历这个Chan来实时包装这些连接
    	type Wrapper interface {
    		Wrap(acceptor.Acceptor) acceptor.Acceptor
    	}
    
    • Agent
      Agent是一个服务端的应用层连接对象,包含了:
      Session信息
      服务器预发送消息队列
      拆解包对象
      最后心跳时间
      停止发送心跳的chan
      关闭发送数据的chan
      全局的关闭信号
      连接对象
      Agent当前状态
      … …
    	type (
    		// Agent corresponds to a user and is used for storing raw Conn information
    		Agent struct {
    			Session            *session.Session  // session
    			appDieChan         chan bool         // app die channel
    			chDie              chan struct{}     // wait for close
    			chSend             chan pendingWrite // push message queue
    			chStopHeartbeat    chan struct{}     // stop heartbeats
    			chStopWrite        chan struct{}     // stop writing messages
    			closeMutex         sync.Mutex
    			conn               net.Conn            // low-level conn fd
    			decoder            codec.PacketDecoder // binary decoder
    			encoder            codec.PacketEncoder // binary encoder
    			heartbeatTimeout   time.Duration
    			lastAt             int64 // last heartbeat unix time stamp
    			messageEncoder     message.Encoder
    			... ...
    			state              int32                // current agent state
    		}
    	
    		pendingWrite struct {
    			ctx  context.Context
    			data []byte
    			err  error
    		}
    	)
    
    • Component
      Component代表业务组件,提供若干个接口
      通过Component生成处理请求的Service
    	type Component interface {
    		Init()
    		AfterInit()
    		BeforeShutdown()
    		Shutdown()
    	}
    
    • Handler、Remote、Service
      Handler和Remote分别代表本地逻辑执行器和远程逻辑执行器
      Service是一组服务对象,包含若干Handler和Remote
      这里有个温柔的细节——Receiver reflect.Value
      pitaya的设计者为了降低引用,采取在逻辑执行器中保留方法的Receiver以达到在Handler和Remote对象中,只需要保存类型的Method,而无需保存带对象引用的Value.Method
    	type (
    		//Handler represents a message.Message's handler's meta information.
    		Handler struct {
    			Receiver    reflect.Value  // receiver of method
    			Method      reflect.Method // method stub
    			Type        reflect.Type   // low-level type of method
    			IsRawArg    bool           // whether the data need to serialize
    			MessageType message.Type   // handler allowed message type (either request or notify)
    		}
    	
    		//Remote represents remote's meta information.
    		Remote struct {
    			Receiver reflect.Value  // receiver of method
    			Method   reflect.Method // method stub
    			HasArgs  bool           // if remote has no args we won't try to serialize received data into arguments
    			Type     reflect.Type   // low-level type of method
    		}
    	
    		// Service implements a specific service, some of it's methods will be
    		// called when the correspond events is occurred.
    		Service struct {
    			Name     string              // name of service
    			Type     reflect.Type        // type of the receiver
    			Receiver reflect.Value       // receiver of methods for the service
    			Handlers map[string]*Handler // registered methods
    			Remotes  map[string]*Remote  // registered remote methods
    			Options  options             // options
    		}
    	)
    
    • Modules
      Modules模块和Component结构一致,唯一的区别在于使用上
      Modules主要是面向系统的一些全局存活的对象
      方便在统一的时机,集中进行启动和关闭
    	type Base struct{}
    	
    	func (c *Base) Init() error {
    		return nil
    	}
    	
    	func (c *Base) AfterInit() {}
    	
    	func (c *Base) BeforeShutdown() {}
    	
    	func (c *Base) Shutdown() error {
    		return nil
    	}
    

    集中管理的对象容器在外部module.go中定义

    	var (
    		modulesMap = make(map[string]interfaces.Module)
    		modulesArr = []moduleWrapper{}
    	)
    	
    	type moduleWrapper struct {
    		module interfaces.Module
    		name   string
    	}
    
    • HandleService
      HandleService就是服务端的主逻辑对象,负责处理一切数据包
      chLocalProcess用于保存待处理的客户端数据包
      chRemoteProcess用于保存待处理的来自其他服务器的数据包
      services注册了处理客户端的服务
      内部聚合一个RemoteService对象,专门负责处理服务器间的数据包
    	type (
    		HandlerService struct {
    			appDieChan         chan bool             // die channel app
    			chLocalProcess     chan unhandledMessage // channel of messages that will be processed locally
    			chRemoteProcess    chan unhandledMessage // channel of messages that will be processed remotely
    			decoder            codec.PacketDecoder   // binary decoder
    			encoder            codec.PacketEncoder   // binary encoder
    			heartbeatTimeout   time.Duration
    			messagesBufferSize int
    			remoteService      *RemoteService
    			serializer         serialize.Serializer          // message serializer
    			server             *cluster.Server               // server obj
    			services           map[string]*component.Service // all registered service
    			messageEncoder     message.Encoder
    			metricsReporters   []metrics.Reporter
    		}
    	
    		unhandledMessage struct {
    			ctx   context.Context
    			agent *agent.Agent
    			route *route.Route
    			msg   *message.Message
    		}
    	)
    
    • RemoteService
      RemoteService中维护服务发现和注册提供的远程服务
    	type RemoteService struct {
    		rpcServer              cluster.RPCServer
    		serviceDiscovery       cluster.ServiceDiscovery
    		serializer             serialize.Serializer
    		encoder                codec.PacketEncoder
    		rpcClient              cluster.RPCClient
    		services               map[string]*component.Service // all registered service
    		router                 *router.Router
    		messageEncoder         message.Encoder
    		server                 *cluster.Server // server obj
    		remoteBindingListeners []cluster.RemoteBindingListener
    	}
    
    • Timer
      Timer模块中维护一个全局定时任务管理者,使用线程安全的map来保存定时任务,通过time.Ticker的chan信号来定期触发
    	var (
    		// Manager manager for all Timers
    		Manager = &struct {
    			incrementID    int64      // auto increment id
    			timers         sync.Map   // all Timers
    			ChClosingTimer chan int64 // timer for closing
    			ChCreatedTimer chan *Timer
    		}{}
    	
    		// Precision indicates the precision of timer, default is time.Second
    		Precision = time.Second
    	
    		// GlobalTicker represents global ticker that all cron job will be executed
    		// in globalTicker.
    		GlobalTicker *time.Ticker
    	)
    
    • pipeline
      pipeline模块提供全局钩子函数的配置
      BeforeHandler 在业务逻辑之前执行
      AfterHandler 在业务逻辑之后执行
    	var (
    		BeforeHandler = &pipelineChannel{}
    		AfterHandler = &pipelineAfterChannel{}
    	)
    	
    	type (
    		HandlerTempl func(ctx context.Context, in interface{}) (out interface{}, err error)
    	
    		AfterHandlerTempl func(ctx context.Context, out interface{}, err error) (interface{}, error)
    	
    		pipelineChannel struct {
    			Handlers []HandlerTempl
    		}
    	
    		pipelineAfterChannel struct {
    			Handlers []AfterHandlerTempl
    		}
    	)
    

    框架流程

    app.go是系统启动的入口
    创建HandlerService
    并根据启动模式如果是集群模式创建RemoteService
    开启服务端事件监听
    开启监听服务器关闭信号的Chan

    	var (
    		app = &App{
    			... ..
    		}
    	
    		remoteService  *service.RemoteService
    		handlerService *service.HandlerService
    	)
    	func Start() {
    		... ..
    		if app.serverMode == Cluster {
    			... ..
    			app.router.SetServiceDiscovery(app.serviceDiscovery)
    	
    			remoteService = service.NewRemoteService(
    				app.rpcClient,
    				app.rpcServer,
    				app.serviceDiscovery,
    				app.router,
    				... ..
    			)
    	
    			app.rpcServer.SetPitayaServer(remoteService)
    	
    			initSysRemotes()
    		}
    	
    		handlerService = service.NewHandlerService(
    			app.dieChan,
    			app.heartbeat,
    			app.server,
    			remoteService,
    			... ..
    		)
    	
    		... ..
    	
    		listen()
    		... ..
    		// stop server
    		select {
    		case <-app.dieChan:
    			logger.Log.Warn("the app will shutdown in a few seconds")
    		case s := <-sg:
    			logger.Log.Warn("got signal: ", s, ", shutting down...")
    			close(app.dieChan)
    		}
    		... ..
    	}
    

    listen方法也就是开启服务,具体包括一下步骤:
    1.注册Component
    2.注册定时任务的GlobalTicker
    3.开启Dispatch处理业务和定时任务(ticket)的goroutine
    4.开启acceptor处理连接的goroutine
    5.开启主逻辑的goroutine
    6.注册Modules

    	func listen() {
    		startupComponents()
    		// create global ticker instance, timer precision could be customized
    		// by SetTimerPrecision
    		timer.GlobalTicker = time.NewTicker(timer.Precision)
    	
    		logger.Log.Infof("starting server %s:%s", app.server.Type, app.server.ID)
    		for i := 0; i < app.config.GetInt("pitaya.concurrency.handler.dispatch"); i++ {
    			go handlerService.Dispatch(i)
    		}
    		for _, acc := range app.acceptors {
    			//acc是迭代中的临时变量,因为要传递协程异步使用所以开辟新变量拷贝引用值
    			//虽然方法接受是interface,不过pitaya在使用处传入的是&的指针,所以修改会同步
    			a := acc
    			go func() {
    				for conn := range a.GetConnChan() {
    					go handlerService.Handle(conn)
    				}
    			}()
    	
    			go func() {
    				a.ListenAndServe()
    			}()
    	
    			logger.Log.Infof("listening with acceptor %s on addr %s", reflect.TypeOf(a), a.GetAddr())
    		}
    		... ..
    		startModules()
    	
    		logger.Log.Info("all modules started!")
    	
    		app.running = true
    	}
    

    startupComponents对Component进行初始化
    然后把Component注册到handlerService和remoteService上

    	func startupComponents() {
    		// component initialize hooks
    		for _, c := range handlerComp {
    			c.comp.Init()
    		}
    	
    		// component after initialize hooks
    		for _, c := range handlerComp {
    			c.comp.AfterInit()
    		}
    	
    		// register all components
    		for _, c := range handlerComp {
    			if err := handlerService.Register(c.comp, c.opts); err != nil {
    				logger.Log.Errorf("Failed to register handler: %s", err.Error())
    			}
    		}
    	
    		// register all remote components
    		for _, c := range remoteComp {
    			if remoteService == nil {
    				logger.Log.Warn("registered a remote component but remoteService is not running! skipping...")
    			} else {
    				if err := remoteService.Register(c.comp, c.opts); err != nil {
    					logger.Log.Errorf("Failed to register remote: %s", err.Error())
    				}
    			}
    		}
    		... ..
    	}
    

    比如HandlerService的注册,反射得到component类型的全部方法,判断isHandlerMethod就加入services里面
    并聚合Component对象的反射Value对象为全部Handler的Method Receiver,减少了对象引用

    	func NewService(comp Component, opts []Option) *Service {
    		s := &Service{
    			Type:     reflect.TypeOf(comp),
    			Receiver: reflect.ValueOf(comp),
    		}
    		... ..
    		return s
    	}
    	
    	func (h *HandlerService) Register(comp component.Component, opts []component.Option) error {
    		s := component.NewService(comp, opts)
    		... ..
    		if err := s.ExtractHandler(); err != nil {
    			return err
    		}
    	
    		h.services[s.Name] = s
    		for name, handler := range s.Handlers {
    			handlers[fmt.Sprintf("%s.%s", s.Name, name)] = handler
    		}
    		return nil
    	}
    	func (s *Service) ExtractHandler() error {
    		typeName := reflect.Indirect(s.Receiver).Type().Name()
    		... ..
    		s.Handlers = suitableHandlerMethods(s.Type, s.Options.nameFunc)
    		... ..
    		for i := range s.Handlers {
    			s.Handlers[i].Receiver = s.Receiver
    		}
    		return nil
    	}
    	func suitableHandlerMethods(typ reflect.Type, nameFunc func(string) string) map[string]*Handler {
    		methods := make(map[string]*Handler)
    		for m := 0; m < typ.NumMethod(); m++ {
    			method := typ.Method(m)
    			mt := method.Type
    			mn := method.Name
    			if isHandlerMethod(method) {
    				... ..
    				handler := &Handler{
    					Method:      method,
    					IsRawArg:    raw,
    					MessageType: msgType,
    				}
    				... ..
    				methods[mn] = handler
    			}
    		}
    		return methods
    	}
    

    handlerService.Dispatch方法负责各种业务的处理,包括:
    1.处理chLocalProcess中的本地Message
    2.使用remoteService处理chRemoteProcess中的远程Message
    3.在定时ticket到达时调用timer.Cron执行定时任务
    4.管理定时任务的创建
    5.管理定时任务的删除

    	func (h *HandlerService) Dispatch(thread int) {
    		defer timer.GlobalTicker.Stop()
    	
    		for {
    			select {
    			case lm := <-h.chLocalProcess:
    				metrics.ReportMessageProcessDelayFromCtx(lm.ctx, h.metricsReporters, "local")
    				h.localProcess(lm.ctx, lm.agent, lm.route, lm.msg)
    	
    			case rm := <-h.chRemoteProcess:
    				metrics.ReportMessageProcessDelayFromCtx(rm.ctx, h.metricsReporters, "remote")
    				h.remoteService.remoteProcess(rm.ctx, nil, rm.agent, rm.route, rm.msg)
    	
    			case <-timer.GlobalTicker.C: // execute cron task
    				timer.Cron()
    	
    			case t := <-timer.Manager.ChCreatedTimer: // new Timers
    				timer.AddTimer(t)
    	
    			case id := <-timer.Manager.ChClosingTimer: // closing Timers
    				timer.RemoveTimer(id)
    			}
    		}
    	}
    

    接下来看看Acceptor的工作,以下为Tcp实现,就是负责接收连接,流入acceptor的Chan

    	func (a *TCPAcceptor) ListenAndServe() {
    		if a.hasTLSCertificates() {
    			a.ListenAndServeTLS(a.certFile, a.keyFile)
    			return
    		}
    	
    		listener, err := net.Listen("tcp", a.addr)
    		if err != nil {
    			logger.Log.Fatalf("Failed to listen: %s", err.Error())
    		}
    		a.listener = listener
    		a.running = true
    		a.serve()
    	}
    	func (a *TCPAcceptor) serve() {
    		defer a.Stop()
    		for a.running {
    			conn, err := a.listener.Accept()
    			if err != nil {
    				logger.Log.Errorf("Failed to accept TCP connection: %s", err.Error())
    				continue
    			}
    	
    			a.connChan <- &tcpPlayerConn{
    				Conn: conn,
    			}
    		}
    	}
    

    前面讲过对于每个Acceptor开启了一个goroutine去处理连接,也就是下面代码

    	for conn := range a.GetConnChan() {
    		go handlerService.Handle(conn)
    	}
    

    所以流入Chan的连接就会被实时的开启一个goroutine去处理,处理过程就是先创建一个Agent对象
    并开启一个goroutine给Agent负责维护连接的心跳
    然后开启死循环,读取连接的数据processPacket

    	func (h *HandlerService) Handle(conn acceptor.PlayerConn) {
    		// create a client agent and startup write goroutine
    		a := agent.NewAgent(conn, h.decoder, h.encoder, h.serializer, h.heartbeatTimeout, h.messagesBufferSize, h.appDieChan, h.messageEncoder, h.metricsReporters)
    	
    		// startup agent goroutine
    		go a.Handle()
    		... ..
    		for {
    			msg, err := conn.GetNextMessage()
    	
    			if err != nil {
    				logger.Log.Errorf("Error reading next available message: %s", err.Error())
    				return
    			}
    	
    			packets, err := h.decoder.Decode(msg)
    			if err != nil {
    				logger.Log.Errorf("Failed to decode message: %s", err.Error())
    				return
    			}
    	
    			if len(packets) < 1 {
    				logger.Log.Warnf("Read no packets, data: %v", msg)
    				continue
    			}
    	
    			// process all packet
    			for i := range packets {
    				if err := h.processPacket(a, packets[i]); err != nil {
    					logger.Log.Errorf("Failed to process packet: %s", err.Error())
    					return
    				}
    			}
    		}
    	}
    

    这时如果使用了pitaya提供的漏桶算法实现的限流wrap来包装acceptor,则会对客户端发送的消息进行限流限速
    这里也是灵活利用for循环遍历chan的特性,所以也是实时地对连接进行包装

    	func (b *BaseWrapper) ListenAndServe() {
    		go b.pipe()
    		b.Acceptor.ListenAndServe()
    	}
    	
    	// GetConnChan returns the wrapper conn chan
    	func (b *BaseWrapper) GetConnChan() chan acceptor.PlayerConn {
    		return b.connChan
    	}
    	
    	func (b *BaseWrapper) pipe() {
    		for conn := range b.Acceptor.GetConnChan() {
    			b.connChan <- b.wrapConn(conn)
    		}
    	}
    	type RateLimitingWrapper struct {
    		BaseWrapper
    	}
    	
    	func NewRateLimitingWrapper(c *config.Config) *RateLimitingWrapper {
    		r := &RateLimitingWrapper{}
    		r.BaseWrapper = NewBaseWrapper(func(conn acceptor.PlayerConn) acceptor.PlayerConn {
    			... ..
    			return NewRateLimiter(conn, limit, interval, forceDisable)
    		})
    		return r
    	}
    	
    	func (r *RateLimitingWrapper) Wrap(a acceptor.Acceptor) acceptor.Acceptor {
    		r.Acceptor = a
    		return r
    	}
    	
    	func (r *RateLimiter) GetNextMessage() (msg []byte, err error) {
    		if r.forceDisable {
    			return r.PlayerConn.GetNextMessage()
    		}
    	
    		for {
    			msg, err := r.PlayerConn.GetNextMessage()
    			if err != nil {
    				return nil, err
    			}
    	
    			now := time.Now()
    			if r.shouldRateLimit(now) {
    				logger.Log.Errorf("Data=%s, Error=%s", msg, constants.ErrRateLimitExceeded)
    				metrics.ReportExceededRateLimiting(pitaya.GetMetricsReporters())
    				continue
    			}
    	
    			return msg, err
    		}
    	}
    

    processPacket对数据包解包后,执行processMessage

    	func (h *HandlerService) processPacket(a *agent.Agent, p *packet.Packet) error {
    		switch p.Type {
    		case packet.Handshake:
    			... ..
    		case packet.HandshakeAck:
    			... ..
    		case packet.Data:
    			if a.GetStatus() < constants.StatusWorking {
    				return fmt.Errorf("receive data on socket which is not yet ACK, session will be closed immediately, remote=%s",
    					a.RemoteAddr().String())
    			}
    			msg, err := message.Decode(p.Data)
    			if err != nil {
    				return err
    			}
    			h.processMessage(a, msg)
    		case packet.Heartbeat:
    			// expected
    		}
    		a.SetLastAt()
    		return nil
    	}
    

    processMessage中包装数据包为unHandledMessage
    根据消息类型,流入chLocalProcess 或者chRemoteProcess 也就转交给上面提到的负责Dispatch的goroutine去处理了

    	func (h *HandlerService) processMessage(a *agent.Agent, msg *message.Message) {
    		requestID := uuid.New()
    		ctx := pcontext.AddToPropagateCtx(context.Background(), constants.StartTimeKey, time.Now().UnixNano())
    		ctx = pcontext.AddToPropagateCtx(ctx, constants.RouteKey, msg.Route)
    		ctx = pcontext.AddToPropagateCtx(ctx, constants.RequestIDKey, requestID.String())
    		tags := opentracing.Tags{
    			"local.id":   h.server.ID,
    			"span.kind":  "server",
    			"msg.type":   strings.ToLower(msg.Type.String()),
    			"user.id":    a.Session.UID(),
    			"request.id": requestID.String(),
    		}
    		ctx = tracing.StartSpan(ctx, msg.Route, tags)
    		ctx = context.WithValue(ctx, constants.SessionCtxKey, a.Session)
    	
    		r, err := route.Decode(msg.Route)
    		... ..
    		message := unhandledMessage{
    			ctx:   ctx,
    			agent: a,
    			route: r,
    			msg:   msg,
    		}
    		if r.SvType == h.server.Type {
    			h.chLocalProcess <- message
    		} else {
    			if h.remoteService != nil {
    				h.chRemoteProcess <- message
    			} else {
    				logger.Log.Warnf("request made to another server type but no remoteService running")
    			}
    		}
    	}
    

    服务器进程启动的最后一步是对全局模块启动

    在外部的module.go文件中,提供了对module的全局注册方法、全部顺序启动方法、全部顺序关闭方法

    	func RegisterModule(module interfaces.Module, name string) error {
    		... ..
    	}
    	
    	func startModules() {
    		for _, modWrapper := range modulesArr {
    			modWrapper.module.Init()
    		}
    		for _, modWrapper := range modulesArr {
    			modWrapper.module.AfterInit()
    		}
    	}
    	
    	func shutdownModules() {
    		for i := len(modulesArr) - 1; i >= 0; i-- {
    			modulesArr[i].module.BeforeShutdown()
    		}
    	
    		for i := len(modulesArr) - 1; i >= 0; i-- {
    			mod := modulesArr[i].module
    			mod.Shutdown()
    		}
    	}
    

    处理细节

    • localProcess
      接下来看看localprocess对于消息的处理细节(为了直观省略部分异常处理代码)
      使用processHandlerMessagef方法对包装出来的ctx对象进行业务操作
      最终根据消息的类型 notify / Request 区分是否需要响应,执行不同处理
    	func (h *HandlerService) localProcess(ctx context.Context, a *agent.Agent, route *route.Route, msg *message.Message) {
    		var mid uint
    		switch msg.Type {
    		case message.Request:
    			mid = msg.ID
    		case message.Notify:
    			mid = 0
    		}
    	
    		ret, err := processHandlerMessage(ctx, route, h.serializer, a.Session, msg.Data, msg.Type, false)
    		if msg.Type != message.Notify {
    			... ..
    			err := a.Session.ResponseMID(ctx, mid, ret)
    			... ..
    		} else {
    			metrics.ReportTimingFromCtx(ctx, h.metricsReporters, handlerType, nil)
    			tracing.FinishSpan(ctx, err)
    		}
    	}
    
    • processHandlerMessage
      这里面负进行业务逻辑
      会先调用executeBeforePipeline(ctx, arg),执行前置的钩子函数
      再通过util.Pcall(h.Method, args)反射调用handler方法
      再调用executeAfterPipeline(ctx, resp, err),执行后置的钩子函数
      最后调用serializeReturn(serializer, resp),对请求结果进行序列化
    	func processHandlerMessage(
    		ctx context.Context,
    		rt *route.Route,
    		serializer serialize.Serializer,
    		session *session.Session,
    		data []byte,
    		msgTypeIface interface{},
    		remote bool,
    	) ([]byte, error) {
    		if ctx == nil {
    			ctx = context.Background()
    		}
    		ctx = context.WithValue(ctx, constants.SessionCtxKey, session)
    		ctx = util.CtxWithDefaultLogger(ctx, rt.String(), session.UID())
    	
    		h, err := getHandler(rt)
    		... ..
    		
    		msgType, err := getMsgType(msgTypeIface)
    		... ..
    		
    		logger := ctx.Value(constants.LoggerCtxKey).(logger.Logger)
    		exit, err := h.ValidateMessageType(msgType)
    		... ..
    		
    		arg, err := unmarshalHandlerArg(h, serializer, data)
    		... ..
    		
    		if arg, err = executeBeforePipeline(ctx, arg); err != nil {
    			return nil, err
    		}
    		... ..
    		
    		args := []reflect.Value{h.Receiver, reflect.ValueOf(ctx)}
    		if arg != nil {
    			args = append(args, reflect.ValueOf(arg))
    		}
    	
    		resp, err := util.Pcall(h.Method, args)
    		if remote && msgType == message.Notify {
    			resp = []byte("ack")
    		}
    	
    		resp, err = executeAfterPipeline(ctx, resp, err)
    		... ..
    	
    		ret, err := serializeReturn(serializer, resp)
    		... ..
    		
    		return ret, nil
    	}
    
    • executeBeforePipeline
      实际就是执行pipeline的BeforeHandler
    	func executeBeforePipeline(ctx context.Context, data interface{}) (interface{}, error) {
    		var err error
    		res := data
    		if len(pipeline.BeforeHandler.Handlers) > 0 {
    			for _, h := range pipeline.BeforeHandler.Handlers {
    				res, err = h(ctx, res)
    				if err != nil {
    					logger.Log.Debugf("pitaya/handler: broken pipeline: %s", err.Error())
    					return res, err
    				}
    			}
    		}
    		return res, nil
    	}
    
    • executeAfterPipeline
      实际就是执行pipeline的AfterHandler
    	func executeAfterPipeline(ctx context.Context, res interface{}, err error) (interface{}, error) {
    		ret := res
    		if len(pipeline.AfterHandler.Handlers) > 0 {
    			for _, h := range pipeline.AfterHandler.Handlers {
    				ret, err = h(ctx, ret, err)
    			}
    		}
    		return ret, err
    	}
    

    util.pcall里展示了golang反射的一种高级用法
    method.Func.Call,第一个参数是Receiver,也就是调用对象方法的实例
    这种设计对比直接保存Value对象的method,反射时直接call,拥有的额外好处就是降低了对象引用,方法不和实例绑定

    	func Pcall(method reflect.Method, args []reflect.Value) (rets interface{}, err error) {
    		... ..
    		r := method.Func.Call(args)
    		if len(r) == 2 {
    			if v := r[1].Interface(); v != nil {
    				err = v.(error)
    			} else if !r[0].IsNil() {
    				rets = r[0].Interface()
    			} else {
    				err = constants.ErrReplyShouldBeNotNull
    			}
    		}
    		return
    	}
    

    更多文章,请搜索公众号歪歪梯Club
    更多资料,请搜索公众号歪歪梯Club

    展开全文
  • 雷达使用Red Pitaya进行射频处理,Raspberry Pi 3/4用于四核信号处理。 最初用于HF的电离层成像,但可以通过频率转换在微波和其他频率下使用。 也可用于。 ::: {.contents} ::: 设置Red Pitaya Radar软件 要将...
  • golang-pitaya服务器框架在win10下架设. 1. 使用git下载pitaya pitaya git地址:https://github.com/topfreegames/pitaya 2.下载安装etcd 下载地址:https://github.com/etcd-io/etcd/releases 寻找最新版本下的...

     golang-pitaya服务器框架在win10下架设.

    1. 使用git下载pitaya

    pitaya git地址:https://github.com/topfreegames/pitaya

    2.下载安装etcd

    下载地址: https://github.com/etcd-io/etcd/releases

    寻找最新版本下的对应windows版本进行下载:

    下载之后,随便解压到一个位置,然后双击运行文件夹下etcd.exe,即可运行启动服务

    3. 下载nats

    分别运行下面两个任务

    # Go client
    go get github.com/nats-io/nats.go/
    
    # Server
    go get github.com/nats-io/nats-server

    安装nats完成后,直接在源码中找到src\github.com\nats-io\nats-server下面的main.go文件打开,使用命令或者是IDE运行此文件既可运行nats服务

    4. 启动pitaya的服务器示例程序

    找到pitaya\examples\demo\chat 下的main.go文件,同样使用命令行或IDE运行此程序既可启动pitaya的聊天示例服务器

    如果使用IDE如goland,需要设置程序工作路径到chat目录下,默认为pitaya根目录

    5.启动客户端

    在浏览器中输入:http://localhost:3251/web/,可以运行聊天客户端

    OK,现在你可以去开心的在windows下玩pitaya了

     

    展开全文
  • m trying to create my own Linux boot image for my Red Pitaya. <p>I've already created my hardware platform on Vivado. The corresponding bitstream was generated successfully and I've already ...
  • Red Pitaya SD card image下载地址 http://downloads.redpitaya.com/downloads/red_pitaya_OS-beta.img.zip Red Pitaya SD card 镜像制作教程原文: ...

    Red Pitaya SD card image下载地址

    http://downloads.redpitaya.com/downloads/red_pitaya_OS-beta.img.zip

    Red Pitaya SD card 镜像制作教程原文:

    https://github.com/RedPitaya/RedPitaya/blob/master/doc/quickStart/SDcard/SDcard.rst

    Windows Red Pitaya SD card 镜像制作

    Windows

    1. Insert SD card into your PC or SD card reader.

    SD卡插入您的PCSD卡读卡器

    1. Download Win32 Disk Imager and extract it.

    下载Win32磁盘成像仪并提取它。

    下载地址1:https://download.csdn.net/download/wangxingxing321/10755807

    下载地址2:https://sourceforge.net/projects/win32diskimager/

     

    1. Open unzipped folder, right-click on the WinDisk32Imager, and select Run as Administrator.

    打开解压文件夹,右键单击WinDisk32Imager,并选择Run as Administrator

     

    1. Under image file box select unzipped Red Pitaya image file.

    在图像文件框下选择unzip压缩红色火龙果图像文件。

    1. Under device box select the drive letter of the SD card.

    在设备框下选择SD卡的驱动器号。

    Note

    请注意

    Be careful to select the correct drive. If you choose the wrong one you risk rasing data from the computer's hard disk! You can easily see the drive letter (for example E:) by looking in the left column of Windows Explorer.

    小心选择正确的驱动器。如果你选错了,你就有可能消除电脑硬盘上的数据! 通过查看Windows资源管理器的左列,您可以很容易地看到驱动器字母(例如E:)

    1. Click Write and wait for the write to complete.

    单击Write,等待写入完成。

    1. Exit the Imager. 退出成像仪。

    Red Pitaya SD card image下载地址

    http://downloads.redpitaya.com/downloads/red_pitaya_OS-beta.img.zip

    Red Pitaya SD card 镜像制作教程原文:

    https://sourceforge.net/projects/win32diskimager/

    Windows Red Pitaya SD card 镜像制作

    Windows

    1. Insert SD card into your PC or SD card reader.

    SD卡插入您的PCSD卡读卡器

    1. Download Win32 Disk Imager and extract it.

    下载Win32磁盘成像仪并提取它。

    下载地址:https://sourceforge.net/projects/win32diskimager/

     

    1. Open unzipped folder, right-click on the WinDisk32Imager, and select Run as Administrator.

    打开解压文件夹,右键单击WinDisk32Imager,并选择Run as Administrator

     

    1. Under image file box select unzipped Red Pitaya image file.

    在图像文件框下选择unzip压缩红色火龙果图像文件。

    1. Under device box select the drive letter of the SD card.

    在设备框下选择SD卡的驱动器号。

    Note

    请注意

    Be careful to select the correct drive. If you choose the wrong one you risk rasing data from the computer's hard disk! You can easily see the drive letter (for example E:) by looking in the left column of Windows Explorer.

    小心选择正确的驱动器。如果你选错了,你就有可能消除电脑硬盘上的数据! 通过查看Windows资源管理器的左列,您可以很容易地看到驱动器字母(例如E:)

    1. Click Write and wait for the write to complete.

    单击Write,等待写入完成。

    1. Exit the Imager. 退出成像仪。

     

    展开全文
  • <ul><li>Device: Red-Pitaya</li><li>SD card image: Alpine Linux for Red-Pitaya sd card, Debian 9 on VM</li><li>Application: SPI and wideband SDR for I-Q baseband.</li><li>Other relevant information:...
  • Red Pitaya是一款价格合理的商业FPGA板,具有快速的模拟输入和输出。 这对于量子光学实验特别是作为模拟系统的数字反馈控制器非常有用。 基于电路板制造商提供的开源软件,PyRPL(Python RedPitaya密码箱)实现了Red...
  • Pitaya是一个简单,快速,轻巧的游戏服务器框架,通过为iOS,Android,Unity等提供集群支持和客户端库。 它为分布式多人游戏和服务器端应用程序提供了基本的开发框架。 入门 先决条件 > = 1.10 (用于服务发现) ...
  • 红火龙果的SD卡映像(red_pitaya_dpll_2017-05-31.zip) 阅读并遵循“ RedPitaya DPLL.pdf的说明和操作手册”文件。 软件版本 所需的Python发行版是WinPython-64bit-3.7.2( )。 FPGA Vivado项目在Vivado 2015.4...
  • Pitaya是基于人工智能技术应用于智能写作的工具,智能校对,智能改写,智能翻译,在线中英双语语法检测校对,实时智能翻译,高效阅读创作一站式智能解决。写作一键智能翻译,阅读素材实时翻译。集成全网海量文本素材...
  • Pitaya-火云术语 v3.0.2

    2019-10-31 08:40:32
    火云术语是国内首个云端翻译辅助软件,专注于术语管理与术语应用以提高翻译准确度和效率,同时提供目前为止国内最大最全的多语术语库以及方便易用的专业查词功能,是适合于专业用户和普通用户的最佳翻译助手。...
  • Red Pitaya javascript 解析

    2019-06-06 15:42:17
    先填坑
  • 8G数据集太大,分开下载(打开我的页面可以下载所有) Fruit Recognition 水果数据集,15个分类,训练识别应用
  • Red Pitaya nginx配置解析

    2019-06-06 15:43:51
    填坑
  • //避免不同的pitaya冲突 服务器必须具有相同的前缀才能相互看到 sd . heartbeatTTL = sd . config . GetDuration ( "pitaya.cluster.sd.etcd.heartbeat.ttl" ) //etcd租约的心跳间隔 sd . logHeartbeat = ...
  • 本地发布新pitaya流程

    2016-10-28 14:52:00
    配置文件也一样,比如在E:\workspace\Pitaya-web\pitaya\src\production-mike文件夹修改了system.properties文件,在D:\apache-tomcat-7.0.70\webapps\pitaya\WEB-INF\classes下的同名文件也要同步修改,可以直接...
  • 图 3-2-3 Pitaya 监控数据示意图 四、总结与展望 Pitaya 是专门为移动端打造的端智能一体化方案,与传统方案相比,具备以下优势: 降低了端智能使用成本,方便业务快速集成,拿到业务收益 完善的动态化能力,支持...
  • iOS 网络请求库

    2021-04-06 16:39:35
    作者johnlui,源码PitayaPitaya 是使用 Swift 编写的,适合大文件上传的 iOS 网络请求库。
  • 把资源包内pitaya.tar.gz放到/tmp文件夹下,并解压 tar -xf pitaya.tar.gz ,并输入 createrepo --database /tmp/pitaya 在/etc/yum.repos.d/下创建pitaya.repo文件,写入内容 [pitaya] name=CentOS-$releas.....
  • fastjson的常用使用总结

    千次阅读 2018-02-27 13:15:21
    现在项目中使用的阿里巴巴的fastjson.package cn.forp.pitaya.zdy.controller; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.alibaba.fastj...
  • SDK调试程序时都是通过JTAG,将PL的bit流文件和应用程序的ELF文件下载到Zynq中,运行查看效果。调试验证功能后需要将程序固化到板子上,使板子上电后可以从存储单元中加载程序,而无需...以火龙果Red Pitaya开发板为...
  • 可以购买红色的Pitaya木板。 为了更好地了解产品规格之间的差异,建议您查看的&支持的功能和应用。 我如何开始使用它? 第一步是按照来连接到您的设备。 如何开始将Red Pitaya用作测试和测量仪器? 从WEB浏览器连接...
  • 火龙果具有润肠、滑肠作用

    千次阅读 2014-01-01 17:24:06
     火龙果,英文Pitaya,本名青龙果、红龙果。原产于中美洲热带。火龙果营养丰富、功能独特,它含有一般植物少有的植物性白蛋白及花青素,丰富的维生素和水溶性膳纤维。  了解更多: >>送福进行时!孕妈大礼包免费...
  • imp pitaya/123456@127.0.0.1:1521/orcl file=D:\pitaya.dmp full=y ignore=y; 文件放在D盘根目录 命令行里面去操作
  • create user pitaya identified by 123456 default tablespace pty; create user boss identified by 123456 default tablespace pty; 授权: grant all privileges to boss; grant all privileges to pitaya; 创建...
  • 二十一 Oracle安装步骤

    2019-05-08 16:27:02
    创建pitaya和boss和pty表空间 create user lsc identified by lsc123456; 创建用户 create user pitaya identified by 123456; 创建用户 create user boss identified by 123456; 创建用户 用户授权: grant....
  • 几乎所有食物的英文翻译 杭州凯艺英语整理编辑: 几乎所有食物的英文翻译 水果类(fruits): 火龙果 pitaya 西红柿 tomato 菠萝 pineapple 西瓜watermelon 香蕉b

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 803
精华内容 321
关键字:

Pitaya