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 }