raccoon icon indicating copy to clipboard operation
raccoon copied to clipboard

Raccoon Client - Go

Open AkbaraliShaikh opened this issue 3 years ago • 0 comments

Summary This reflects on how the raccoon-go-client can be used to communicate with the raccoon service using the different communication protocols.

Proposed solution

The idea is to keep the initialisations of the WebSocket, HTTP, and GRPC clients distinct so that users can select the one they are interested in using.

Message Encoding: The client will take care of the serialization/deserialization of the JSON and PROTO messages. The user does not have to worry about it; the client will handle it.

Observability:

Client Stats using StatsD: The client is also intending to provide the stats for API calls. The following stats are planned for export to the user.

sent_tt_ms
ack_tt_ms
total_bytes_sent
total_bytes_recieved
total_conn_err

Logging:

The client provides the interface and the default console logging that the user can use, disable, or provide its own implementation.

Client Info (version.go):

The client will emit the following information about itself, which will be provided during the go build command using the “-ldflags” Name Version BuildDate

Request Guid:

The client will auto generate the request_guid. The client will also provide the provision to pass the request_guid.

Serialization:

The user can pass the array of high level proto/json etc. to the send API, and the client will internally serialize the user data and wrap it for the raccoon request. The client comes with Json/Proto serializers built in, and even users can configure any serialization.

Websocket

Event Ack:

In the case of WebSocket, the client will also provide the async delivery channel to which the user can subscribe to receive the event acknowledgements.

Ping/Pong -:

The client will internally handle the ping/pong communication with the raccoon, will provide the setting for interval time.

Retry

The user can configure the retry options on which the client will retry based on the configuration provided.

This proposal gives the user the flexibility to use any serialization for converting the event bytes before sending them to the client, default will be proto.

The client will ship the Json/Proto serializer as part of the package.

   // Request message
   type Event struct {
       Type string
       Data interface{}
   }
 
   // Response message
   type Response struct {
       Status   string
       SentTime int64
       Reason   string
       Data     map[string]string
   }
 
   type Client interface {
       Send([]*Event) (string, *Response, error)
       SetUserClientInfo(*App)
   }
 
   // interfaces
   type WebSocket interface {
       Client
       // WebSocket specific methods
   }
 
   type Grpc interface { Client }
   type Rest interface { Client }
   type RestClient struct { Opts Options }
 
   type ClientOption interface {
       Apply(*Options)
   }
 
   type Options struct {
       Ser Serializer
   }
 
   func (s *RestClient) Apply(op *Options) {
       s.serialize = op.Ser
   }
 
  func WithSerializer(ser Serializer) ClientOption {
       return &RestClient{ serialize: ser }
   }
 
   
   // NewRest creates the rest client.
   func NewRest(url string, opts ...ClientOption) *RestClient {
       var o Options
       for _, opt := range opts {
           opt.Apply(&o)
       }
       return &RestClient{
           Serialize: o.Serializer,
       }
   }
 
   // Serializer
   type Serializer func(interface{}) ([]byte, error)
 
   // set the app info
   func (*RestClient) SetAppInfo(app *App) {}
 
   // Send sends the events to the raccoon service
   func (c *RestClient) Send(events []*Event) (string, *Response, error) {
       e := []*pb.Event{}
       for _, ev := range events {
           // serialize the bytes based on the config
           b, _ := c.serialize(ev.Data)
           e = append(e, &pb.Event{
               EventBytes: b,
               Type:       ev.Type,
           })
       }
       reqId := uuid.NewString()
       raccoonReq := &pb.SendEventRequest{
           ReqGuid: reqId,
           Events:  e,
       }
       // log & Send racoonReq to raccoon service
       log.Print(raccoonReq)
 
       return reqId, &Response{}, nil
   }
 
   // Client usage example
   func ClientExample() {
 
       JSON := func(i interface{}) ([]byte, error) {
           return json.Marshal(i)
       }
 
       rest := NewRest("http://localhost:8080",
           WithSerializer(JSON),
       )
 
       rest.SetAppInfo(&App{
           Id:      "goplay-123",
           Name:    "goplay-backend",
           Version: "1.0",
       })
 
       reqId, resp, err := rest.Send([]*Event{
           &Event{
               Type: "page",
               Data: &struct {
                   name string
               }{"page-name"},
           },
       })
 
       if err != nil {
           log.Printf("%v, %s", err, reqId)
       }
 
       log.Print(resp.Status)
   }

AkbaraliShaikh avatar Sep 16 '22 10:09 AkbaraliShaikh