1111//
1212// Features:
1313// - HTTP and WebSocket endpoint support
14- // - Automatic method resolution and dispatch
14+ // - Automatic method resolution and dispatch with cached lookups
1515// - Protocol Buffer JSON marshaling/unmarshaling
1616// - Multiple streaming patterns (unary, server, client, bidirectional)
1717// - Context enrichment with HTTP and WebSocket components
2121// 1. Define your service in a .proto file
2222// 2. Generate Go code using protoc with the protoc-gen-jrpc plugin
2323// 3. Implement the generated Server interface
24- // 4. Create a new jRPC service with jrpc.New (yourServer)
24+ // 4. Create a new jRPC service with jrpc.Register (yourServer)
2525// 5. Register the HandlerFunc with the web package function WithJRPC
2626//
2727// Example:
5050// err := web.Instance().
5151// WithHost("localhost").
5252// WithPort(8080).
53- // WithJRPC(jrpc.New (&MyService{})).
53+ // WithJRPC(jrpc.Register (&MyService{})).
5454// Start().Error
5555// if err != nil {
5656// log.Fatal().Err(err).Msg("server exited")
@@ -103,6 +103,7 @@ const (
103103// protocol buffer message handling, and context enrichment.
104104type Service struct {
105105 Server
106+ methods map [string ]protoreflect.MethodDescriptor // cached method descriptors for faster lookup
106107}
107108
108109// Server represents a jRPC service implementation.
@@ -111,10 +112,29 @@ type Server interface {
111112 Descriptor () protoreflect.FileDescriptor
112113}
113114
114- // New creates a new jrpc service instance and registers the provided
115+ // Register creates a new jrpc service instance and registers the provided
115116// service implementation. The service implementation has to implement the Descriptor method.
116- func New (s Server ) * Service {
117- return & Service {Server : s }
117+ // This function builds a method cache for improved lookup performance.
118+ func Register (s Server ) * Service {
119+ service := & Service {
120+ Server : s ,
121+ methods : make (map [string ]protoreflect.MethodDescriptor ),
122+ }
123+
124+ // Build the methods cache
125+ services := s .Descriptor ().Services ()
126+ for i := 0 ; i < services .Len (); i ++ {
127+ serviceDesc := services .Get (i )
128+ methods := serviceDesc .Methods ()
129+ for j := 0 ; j < methods .Len (); j ++ {
130+ methodDesc := methods .Get (j )
131+ // Use fully qualified method name as key: service.method
132+ key := string (serviceDesc .Name ()) + "." + string (methodDesc .Name ())
133+ service .methods [key ] = methodDesc
134+ }
135+ }
136+
137+ return service
118138}
119139
120140// SetUpgrader allows setting a custom WebSocket upgrader with specific options.
@@ -404,6 +424,13 @@ func (s *Service) call(ctx context.Context, method string, req proto.Message) (a
404424}
405425
406426func (s * Service ) find (service , method string ) (protoreflect.MethodDescriptor , error ) {
427+ // Use cached method lookup for better performance
428+ key := service + "." + method
429+ if md , exists := s .methods [key ]; exists {
430+ return md , nil
431+ }
432+
433+ // Fallback to dynamic lookup if not found in cache (should not happen in normal operation)
407434 sd := s .Descriptor ().Services ().ByName (protoreflect .Name (service ))
408435 if sd == nil {
409436 return nil , apperror .NewError ("service not found" )
0 commit comments