github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/23_proto_actor/cluster-metrics/shared/protos_protoactor.go (about)

     1  package shared
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"math"
     8  	"time"
     9  
    10  	"github.com/AsynkronIT/protoactor-go/actor"
    11  	"github.com/AsynkronIT/protoactor-go/cluster"
    12  	"github.com/AsynkronIT/protoactor-go/remote"
    13  	"github.com/gogo/protobuf/proto"
    14  )
    15  
    16  var _ = proto.Marshal
    17  var _ = fmt.Errorf
    18  var _ = math.Inf
    19  
    20  var rootContext = actor.EmptyRootContext
    21  
    22  var xHelloFactory func() Hello
    23  
    24  // HelloFactory produces a Hello
    25  func HelloFactory(factory func() Hello) {
    26  	xHelloFactory = factory
    27  }
    28  
    29  // GetHelloGrain instantiates a new HelloGrain with given ID
    30  func GetHelloGrain(id string) *HelloGrain {
    31  	return &HelloGrain{ID: id}
    32  }
    33  
    34  // Hello interfaces the services available to the Hello
    35  type Hello interface {
    36  	Init(id string)
    37  	Terminate()
    38  
    39  	SayHello(*HelloRequest, cluster.GrainContext) (*HelloResponse, error)
    40  
    41  	Add(*AddRequest, cluster.GrainContext) (*AddResponse, error)
    42  
    43  	VoidFunc(*AddRequest, cluster.GrainContext) (*Unit, error)
    44  }
    45  
    46  // HelloGrain holds the base data for the HelloGrain
    47  type HelloGrain struct {
    48  	ID string
    49  }
    50  
    51  // SayHello requests the execution on to the cluster using default options
    52  func (g *HelloGrain) SayHello(r *HelloRequest) (*HelloResponse, error) {
    53  	return g.SayHelloWithOpts(r, cluster.DefaultGrainCallOptions())
    54  }
    55  
    56  // SayHelloWithOpts requests the execution on to the cluster
    57  func (g *HelloGrain) SayHelloWithOpts(r *HelloRequest, opts *cluster.GrainCallOptions) (*HelloResponse, error) {
    58  	fun := func() (*HelloResponse, error) {
    59  		pid, statusCode := cluster.Get(g.ID, "Hello")
    60  		if statusCode != remote.ResponseStatusCodeOK {
    61  			return nil, fmt.Errorf("get PID failed with StatusCode: %v", statusCode)
    62  		}
    63  		bytes, err := proto.Marshal(r)
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  		request := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes}
    68  		response, err := rootContext.RequestFuture(pid, request, opts.Timeout).Result()
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		switch msg := response.(type) {
    73  		case *cluster.GrainResponse:
    74  			result := &HelloResponse{}
    75  			err = proto.Unmarshal(msg.MessageData, result)
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  			return result, nil
    80  		case *cluster.GrainErrorResponse:
    81  			return nil, errors.New(msg.Err)
    82  		default:
    83  			return nil, errors.New("unknown response")
    84  		}
    85  	}
    86  
    87  	var res *HelloResponse
    88  	var err error
    89  	for i := 0; i < opts.RetryCount; i++ {
    90  		res, err = fun()
    91  		if err == nil || err.Error() != "future: timeout" {
    92  			return res, err
    93  		} else if opts.RetryAction != nil {
    94  			opts.RetryAction(i)
    95  		}
    96  	}
    97  	return nil, err
    98  }
    99  
   100  // SayHelloChan allows to use a channel to execute the method using default options
   101  func (g *HelloGrain) SayHelloChan(r *HelloRequest) (<-chan *HelloResponse, <-chan error) {
   102  	return g.SayHelloChanWithOpts(r, cluster.DefaultGrainCallOptions())
   103  }
   104  
   105  // SayHelloChanWithOpts allows to use a channel to execute the method
   106  func (g *HelloGrain) SayHelloChanWithOpts(r *HelloRequest, opts *cluster.GrainCallOptions) (<-chan *HelloResponse, <-chan error) {
   107  	c := make(chan *HelloResponse)
   108  	e := make(chan error)
   109  	go func() {
   110  		res, err := g.SayHelloWithOpts(r, opts)
   111  		if err != nil {
   112  			e <- err
   113  		} else {
   114  			c <- res
   115  		}
   116  		close(c)
   117  		close(e)
   118  	}()
   119  	return c, e
   120  }
   121  
   122  // Add requests the execution on to the cluster using default options
   123  func (g *HelloGrain) Add(r *AddRequest) (*AddResponse, error) {
   124  	return g.AddWithOpts(r, cluster.DefaultGrainCallOptions())
   125  }
   126  
   127  // AddWithOpts requests the execution on to the cluster
   128  func (g *HelloGrain) AddWithOpts(r *AddRequest, opts *cluster.GrainCallOptions) (*AddResponse, error) {
   129  	fun := func() (*AddResponse, error) {
   130  		pid, statusCode := cluster.Get(g.ID, "Hello")
   131  		if statusCode != remote.ResponseStatusCodeOK {
   132  			return nil, fmt.Errorf("get PID failed with StatusCode: %v", statusCode)
   133  		}
   134  		bytes, err := proto.Marshal(r)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		request := &cluster.GrainRequest{MethodIndex: 1, MessageData: bytes}
   139  		response, err := rootContext.RequestFuture(pid, request, opts.Timeout).Result()
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  		switch msg := response.(type) {
   144  		case *cluster.GrainResponse:
   145  			result := &AddResponse{}
   146  			err = proto.Unmarshal(msg.MessageData, result)
   147  			if err != nil {
   148  				return nil, err
   149  			}
   150  			return result, nil
   151  		case *cluster.GrainErrorResponse:
   152  			return nil, errors.New(msg.Err)
   153  		default:
   154  			return nil, errors.New("unknown response")
   155  		}
   156  	}
   157  
   158  	var res *AddResponse
   159  	var err error
   160  	for i := 0; i < opts.RetryCount; i++ {
   161  		res, err = fun()
   162  		if err == nil || err.Error() != "future: timeout" {
   163  			return res, err
   164  		} else if opts.RetryAction != nil {
   165  			opts.RetryAction(i)
   166  		}
   167  	}
   168  	return nil, err
   169  }
   170  
   171  // AddChan allows to use a channel to execute the method using default options
   172  func (g *HelloGrain) AddChan(r *AddRequest) (<-chan *AddResponse, <-chan error) {
   173  	return g.AddChanWithOpts(r, cluster.DefaultGrainCallOptions())
   174  }
   175  
   176  // AddChanWithOpts allows to use a channel to execute the method
   177  func (g *HelloGrain) AddChanWithOpts(r *AddRequest, opts *cluster.GrainCallOptions) (<-chan *AddResponse, <-chan error) {
   178  	c := make(chan *AddResponse)
   179  	e := make(chan error)
   180  	go func() {
   181  		res, err := g.AddWithOpts(r, opts)
   182  		if err != nil {
   183  			e <- err
   184  		} else {
   185  			c <- res
   186  		}
   187  		close(c)
   188  		close(e)
   189  	}()
   190  	return c, e
   191  }
   192  
   193  // VoidFunc requests the execution on to the cluster using default options
   194  func (g *HelloGrain) VoidFunc(r *AddRequest) (*Unit, error) {
   195  	return g.VoidFuncWithOpts(r, cluster.DefaultGrainCallOptions())
   196  }
   197  
   198  // VoidFuncWithOpts requests the execution on to the cluster
   199  func (g *HelloGrain) VoidFuncWithOpts(r *AddRequest, opts *cluster.GrainCallOptions) (*Unit, error) {
   200  	fun := func() (*Unit, error) {
   201  		pid, statusCode := cluster.Get(g.ID, "Hello")
   202  		if statusCode != remote.ResponseStatusCodeOK {
   203  			return nil, fmt.Errorf("get PID failed with StatusCode: %v", statusCode)
   204  		}
   205  		bytes, err := proto.Marshal(r)
   206  		if err != nil {
   207  			return nil, err
   208  		}
   209  		request := &cluster.GrainRequest{MethodIndex: 2, MessageData: bytes}
   210  		response, err := rootContext.RequestFuture(pid, request, opts.Timeout).Result()
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  		switch msg := response.(type) {
   215  		case *cluster.GrainResponse:
   216  			result := &Unit{}
   217  			err = proto.Unmarshal(msg.MessageData, result)
   218  			if err != nil {
   219  				return nil, err
   220  			}
   221  			return result, nil
   222  		case *cluster.GrainErrorResponse:
   223  			return nil, errors.New(msg.Err)
   224  		default:
   225  			return nil, errors.New("unknown response")
   226  		}
   227  	}
   228  
   229  	var res *Unit
   230  	var err error
   231  	for i := 0; i < opts.RetryCount; i++ {
   232  		res, err = fun()
   233  		if err == nil || err.Error() != "future: timeout" {
   234  			return res, err
   235  		} else if opts.RetryAction != nil {
   236  			opts.RetryAction(i)
   237  		}
   238  	}
   239  	return nil, err
   240  }
   241  
   242  // VoidFuncChan allows to use a channel to execute the method using default options
   243  func (g *HelloGrain) VoidFuncChan(r *AddRequest) (<-chan *Unit, <-chan error) {
   244  	return g.VoidFuncChanWithOpts(r, cluster.DefaultGrainCallOptions())
   245  }
   246  
   247  // VoidFuncChanWithOpts allows to use a channel to execute the method
   248  func (g *HelloGrain) VoidFuncChanWithOpts(r *AddRequest, opts *cluster.GrainCallOptions) (<-chan *Unit, <-chan error) {
   249  	c := make(chan *Unit)
   250  	e := make(chan error)
   251  	go func() {
   252  		res, err := g.VoidFuncWithOpts(r, opts)
   253  		if err != nil {
   254  			e <- err
   255  		} else {
   256  			c <- res
   257  		}
   258  		close(c)
   259  		close(e)
   260  	}()
   261  	return c, e
   262  }
   263  
   264  // HelloActor represents the actor structure
   265  type HelloActor struct {
   266  	inner   Hello
   267  	Timeout *time.Duration
   268  }
   269  
   270  // Receive ensures the lifecycle of the actor for the received message
   271  func (a *HelloActor) Receive(ctx actor.Context) {
   272  	switch msg := ctx.Message().(type) {
   273  	case *actor.Started:
   274  		a.inner = xHelloFactory()
   275  		id := ctx.Self().Id
   276  		a.inner.Init(id[7:]) // skip "remote$"
   277  		if a.Timeout != nil {
   278  			ctx.SetReceiveTimeout(*a.Timeout)
   279  		}
   280  	case *actor.ReceiveTimeout:
   281  		a.inner.Terminate()
   282  		rootContext.PoisonFuture(ctx.Self()).Wait()
   283  
   284  	case actor.AutoReceiveMessage: // pass
   285  	case actor.SystemMessage: // pass
   286  
   287  	case *cluster.GrainRequest:
   288  		switch msg.MethodIndex {
   289  
   290  		case 0:
   291  			req := &HelloRequest{}
   292  			err := proto.Unmarshal(msg.MessageData, req)
   293  			if err != nil {
   294  				log.Fatalf("[GRAIN] proto.Unmarshal failed %v", err)
   295  			}
   296  			r0, err := a.inner.SayHello(req, ctx)
   297  			if err == nil {
   298  				bytes, errMarshal := proto.Marshal(r0)
   299  				if errMarshal != nil {
   300  					log.Fatalf("[GRAIN] proto.Marshal failed %v", errMarshal)
   301  				}
   302  				resp := &cluster.GrainResponse{MessageData: bytes}
   303  				ctx.Respond(resp)
   304  			} else {
   305  				resp := &cluster.GrainErrorResponse{Err: err.Error()}
   306  				ctx.Respond(resp)
   307  			}
   308  
   309  		case 1:
   310  			req := &AddRequest{}
   311  			err := proto.Unmarshal(msg.MessageData, req)
   312  			if err != nil {
   313  				log.Fatalf("[GRAIN] proto.Unmarshal failed %v", err)
   314  			}
   315  			r0, err := a.inner.Add(req, ctx)
   316  			if err == nil {
   317  				bytes, errMarshal := proto.Marshal(r0)
   318  				if errMarshal != nil {
   319  					log.Fatalf("[GRAIN] proto.Marshal failed %v", errMarshal)
   320  				}
   321  				resp := &cluster.GrainResponse{MessageData: bytes}
   322  				ctx.Respond(resp)
   323  			} else {
   324  				resp := &cluster.GrainErrorResponse{Err: err.Error()}
   325  				ctx.Respond(resp)
   326  			}
   327  
   328  		case 2:
   329  			req := &AddRequest{}
   330  			err := proto.Unmarshal(msg.MessageData, req)
   331  			if err != nil {
   332  				log.Fatalf("[GRAIN] proto.Unmarshal failed %v", err)
   333  			}
   334  			r0, err := a.inner.VoidFunc(req, ctx)
   335  			if err == nil {
   336  				bytes, errMarshal := proto.Marshal(r0)
   337  				if errMarshal != nil {
   338  					log.Fatalf("[GRAIN] proto.Marshal failed %v", errMarshal)
   339  				}
   340  				resp := &cluster.GrainResponse{MessageData: bytes}
   341  				ctx.Respond(resp)
   342  			} else {
   343  				resp := &cluster.GrainErrorResponse{Err: err.Error()}
   344  				ctx.Respond(resp)
   345  			}
   346  
   347  		}
   348  	default:
   349  		log.Printf("Unknown message %v", msg)
   350  	}
   351  }