github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/actor/props.go (about) 1 package actor 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "log/slog" 8 9 "github.com/asynkron/protoactor-go/metrics" 10 "go.opentelemetry.io/otel" 11 "go.opentelemetry.io/otel/metric" 12 ) 13 14 type ( 15 SpawnFunc func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (*PID, error) 16 ReceiverMiddleware func(next ReceiverFunc) ReceiverFunc 17 SenderMiddleware func(next SenderFunc) SenderFunc 18 ContextDecorator func(next ContextDecoratorFunc) ContextDecoratorFunc 19 SpawnMiddleware func(next SpawnFunc) SpawnFunc 20 ) 21 22 // Default values. 23 var ( 24 defaultDispatcher = NewDefaultDispatcher(300) 25 defaultMailboxProducer = Unbounded() 26 defaultSpawner = func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (*PID, error) { 27 ctx := newActorContext(actorSystem, props, parentContext.Self()) 28 mb := props.produceMailbox() 29 30 // prepare the mailbox number counter 31 if ctx.actorSystem.Config.MetricsProvider != nil { 32 sysMetrics, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) 33 if ok && sysMetrics.enabled { 34 if instruments := sysMetrics.metrics.Get(metrics.InternalActorMetrics); instruments != nil { 35 sysMetrics.PrepareMailboxLengthGauge() 36 meter := otel.Meter(metrics.LibName) 37 38 if _, err := meter.RegisterCallback(func(_ context.Context, o metric.Observer) error { 39 o.ObserveInt64(instruments.ActorMailboxLength, int64(mb.UserMessageCount()), metric.WithAttributes(sysMetrics.CommonLabels(ctx)...)) 40 return nil 41 }); err != nil { 42 err = fmt.Errorf("failed to instrument Actor Mailbox, %w", err) 43 actorSystem.Logger().Error(err.Error(), slog.Any("error", err)) 44 } 45 } 46 } 47 } 48 49 dp := props.getDispatcher() 50 proc := NewActorProcess(mb) 51 pid, absent := actorSystem.ProcessRegistry.Add(proc, id) 52 if !absent { 53 return pid, ErrNameExists 54 } 55 ctx.self = pid 56 57 initialize(props, ctx) 58 59 mb.RegisterHandlers(ctx, dp) 60 mb.PostSystemMessage(startedMessage) 61 mb.Start() 62 63 return pid, nil 64 } 65 defaultContextDecorator = func(ctx Context) Context { 66 return ctx 67 } 68 ) 69 70 func initialize(props *Props, ctx *actorContext) { 71 if props.onInit == nil { 72 return 73 } 74 75 for _, init := range props.onInit { 76 init(ctx) 77 } 78 } 79 80 // DefaultSpawner this is a hacking way to allow Proto.Router access default spawner func. 81 var DefaultSpawner SpawnFunc = defaultSpawner 82 83 // ErrNameExists is the error used when an existing name is used for spawning an actor. 84 var ErrNameExists = errors.New("spawn: name exists") 85 86 // Props represents configuration to define how an actor should be created. 87 type Props struct { 88 spawner SpawnFunc 89 producer ProducerWithActorSystem 90 mailboxProducer MailboxProducer 91 guardianStrategy SupervisorStrategy 92 supervisionStrategy SupervisorStrategy 93 dispatcher Dispatcher 94 receiverMiddleware []ReceiverMiddleware 95 senderMiddleware []SenderMiddleware 96 spawnMiddleware []SpawnMiddleware 97 receiverMiddlewareChain ReceiverFunc 98 senderMiddlewareChain SenderFunc 99 spawnMiddlewareChain SpawnFunc 100 contextDecorator []ContextDecorator 101 contextDecoratorChain ContextDecoratorFunc 102 onInit []func(ctx Context) 103 } 104 105 func (props *Props) getSpawner() SpawnFunc { 106 if props.spawner == nil { 107 return defaultSpawner 108 } 109 110 return props.spawner 111 } 112 113 func (props *Props) getDispatcher() Dispatcher { 114 if props.dispatcher == nil { 115 return defaultDispatcher 116 } 117 118 return props.dispatcher 119 } 120 121 func (props *Props) getSupervisor() SupervisorStrategy { 122 if props.supervisionStrategy == nil { 123 return defaultSupervisionStrategy 124 } 125 126 return props.supervisionStrategy 127 } 128 129 func (props *Props) getContextDecoratorChain() ContextDecoratorFunc { 130 if props.contextDecoratorChain == nil { 131 return defaultContextDecorator 132 } 133 134 return props.contextDecoratorChain 135 } 136 137 func (props *Props) produceMailbox() Mailbox { 138 if props.mailboxProducer == nil { 139 return defaultMailboxProducer() 140 } 141 142 return props.mailboxProducer() 143 } 144 145 func (props *Props) spawn(actorSystem *ActorSystem, name string, parentContext SpawnerContext) (*PID, error) { 146 return props.getSpawner()(actorSystem, name, props, parentContext) 147 } 148 149 func (props *Props) Configure(opts ...PropsOption) *Props { 150 for _, opt := range opts { 151 opt(props) 152 } 153 154 return props 155 }