Is it OK to use gRPC to push data? - go

I'm wondering if it's a good idea to push data from gRPC server to a client. Basically I want to use a pub/sub pattern with gRPC.
The way I do is that I return a response stream on the server implementation that I never close. Then, the client has a never ending go routine in charge of reading this stream.
Here is an example:
service Service {
rpc RegularChanges (Void) returns (stream Change) {}
}
On the server side:
func (self *MyServiceImpl) RegularChanges(in *pb.Void, stream pb.Service_RegularChangesServer) error {
for {
d, err := time.ParseDuration("1s")
if err != nil {
log.Fatalf("Cannot parse duration")
break;
}
time.Sleep(d)
stream.Send(&pb.Change{Name:"toto", Description:"status changed"})
}
return nil
}
On client:
for {
change, err := streamChanges.Recv()
if err != nil {
log.Fatalf("Error retrieving change")
} else {
log.Println(change)
}
}
I just began with go and gRPC but I know it's based on HTTP2, hence it should support pushing datas. However, I'm not sure this is the way gRPC should be used.

gRPC is intended to be used in this way.
You should still consider how the client should behave on failures and how you may want to re-balance across backends. If your connection is going across the Internet, you may also want to enable keepalive to detect connection breakages by providing KeepaliveParams to the client and server.

Related

Using google PubSub with a Cloud Run gRPC service

I have a Cloud Run gRPC service and I want it to listen to PubSub topic.
The only way to do it from the official documentations is to use a trigger but this only works with REST server that accept http requests.
I couldn't find any good example on the web of how to use pubsub with cloud run grpc services.
My service is built with Go following the official grpc instructions
main.go:
func main() {
ctx := context.Background()
pubSubClient, err := pubsub.NewClient(ctx, 'project-id')
if err != nil {
log.Fatal(err)
}
pubSubSubscription := pubSubClient.Subscription("subscription-id")
go func(sub *pubsub.Subscription) {
err := sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
defer m.Ack()
log.Printf("sub.Receiv called %+v", m)
})
if err != nil {
// handle error
}
}(pubSubSubscription)
....
lis, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s, _ := server.New(ctx, ...)
grpcServer := grpc.NewServer()
grpcpkg.RegisterReportServiceServer(grpcServer, s)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
This will work as long as there is at least one instance up and listening. But if there is no instances active, and a new pubsub message publishes, cloud run won't "wake up" the insane because it's autoscaling is utilized based on http requests.
The only way to make the above code work is by setting min-instance 1 in cloud run, but this can be not efficient for a lot of use cases (i.e the service is not active at night)
Is there any work around for this?
Is there any way to trigger a grpc cloud run service from a pubsub message?
The sub.Receive method you are attempting to use on the client library is meant for Pull message delivery and not Push delivery. You can read about how these two delivery types compare to make a choice.
If you wish to receive messages as incoming requests, you must use Push delivery and can follow the the Cloud Run Pubsub usage guide. Note that Cloud Pub/Sub only delivers messages as HTTPS POST requests with the specified JSON payload. To handle these requests from a gRPC server, you can use the gRPC Gateway plugin to generate a reverse proxy that exposes an HTTP API.
Once your Cloud Run instance is accepting HTTP traffic using the reverse proxy, it will be autoscaled with incoming requests, so you won't need to keep 1 instance always running. Note that this may mean that message processing latency is upto 10s on a cold start.

GRPC: when get a new client?

I am using grpc in my project, if i have a grpc service call helloService, should i use GetNewHelloServiceClient to get a new client in every function? Or just get once in start program?
// for example:
c.GET("/hello", SayHello)
func SayHello() {
c := pb.GetNewHelloServiceClient()
res, err := c.SayHello(context.Background(), &request)
if err != nil {
return
}
fmt.print(res.Hello)
}
Create a gRPC client just once.
Lots of networking concepts in go are designed for reuse: http clients, http transports, sql.DB connection pools etc. They are all go-routine safe & should only be created once but reused many times.

Got 4xx Error while subscribing on Podio Push service in go concurrent pattern

I'm experiencing some unexpected errors while trying to subscribe on Podio Push service. I use golang concurrency pattern defined here and here is the bayeux client library used for subscription.
Basically the flow tries to retrieve the item first and then subscribe into push object provided with the item object. There is channel object where i store each task (taskLoad: ~each item_id with credentials it needs for retrieval)
item := new(podio.Item)
item, err = podio.GetItem(itemId)
if err != nil {
log.Errorf("PODIO", "Could not get item %d -> %s", itemId, err)
return
}
and, later inside another func
messages := make(chan *bayeux.Message)
server := GetBayeux()
defer server.Close()
if err = push.Subscribe(server, messages); err != nil {
// log err, with item details
log.Errorf("PODIO", "%s", err, push)
// re-enqueue the task in taskLoad channel
go enqueueTask(true, messages, sigrepeat, timer)
// release sigwait channel on subscription error
<-sigwait
return
}
here GetBayeux func is just a singleton which wraps the client
func GetBayeux() *bayeux.Client {
bayeuxOnce.Do(func() {
Bayeux = bayeux.NewClient("https://push.podio.com/faye", nil)
})
return Bayeux
}
there is about ~15000 items to listen and I should subscribe to each of them but unfortunately sometimes I got one of these errors while processing subscriptions
401:k9jx3v4qq276k9q84gokpqirhjrfbyd:Unknown client [{"channel":"/item/9164xxxxx","signature":"46bd8ab3ef2a31297d8f4f5ddxxxx","timestamp":"2018-01-02T14:34:02Z","expires_in":21600}]
OR
HTTP Status 400 [{"channel":"/item/9158xxxxx","signature":"31bf8b4697ca2fda69bd7fd532d08xxxxxx","timestamp":"2018-01-02T14:37:02Z","expires_in":21600}]
OR
[WRN] Bayeux connect failed: HTTP Status 400
OR
Bayeux connect failed: Post https://push.podio.com/faye: http2: server sent GOAWAY and closed the connection; LastStreamID=1999, ErrCode=NO_ERROR, debug=""
So now, i'd like to know why i got these errors and most of all how i can fix them to ensure to listen to all items in the scope.
If anyone knows, is there any limitation about concurrent access into podio push service?
Thanks
UPDATE 2019-01-07
it was the singleton that messed the process. as it was in a goroutine context there was some subscriptions that was not allowed because the server has been closed by another goroutine. The fix was to exposing Unsubscribe method and use it instead of Close method which disconnect the client from the server.
defer server.Close()
became
defer push.Unsubscribe(server)

How to ensure redis subscriber receive message in Go (Golang)?

I'm using gin framework to build an API server. In General, I'm build 2 projects. Project 'API' and Project 'SOCKET'. Project 'API' is the main REST API that will used in Android, developed using gin framework (golang). And Project 'SOCKET' is the socket server for client that will use socket connection , using node.js (Socket.IO)
The process begin like this :
User A : as the requester ; A connect to "API"
User B : as the responder ; B connect to "SOCKET"
User A call API requestData from android, the request will handled by "API"'s project. And Project "API" will record the request, and publish on redis
as new_request using pubsub
this is the code for example :
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
pong, err := client.Ping().Result()
fmt.Println(pong, err)
if err !=nil {
fmt.Println("err",err);
}
pubsub, err := client.Subscribe("responseclient")
if err !=nil {
panic(err)
}
defer pubsub.Close()
err = client.Publish("new_request", "Example New Request").Err()
if err !=nil {
panic(err)
}
msg, err :=pubsub.ReceiveMessage()
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
}
In Project "SOCKET" there is a subscriber that will listen every publish that occured, and publish new message to channel responseclient
this is for the example code :
ioApp.on ('connection' , function(socket) {
redisSub.on('new_request', function (channel, message) {
console.log(channel + ':' + message);
redisPub.publish("responseclient", JSON.stringify(res));
});
})
This work smoothly, if User B is Connected to Socket.IO. But if User B was offline, or not connected to socket.io, this will waiting for long, until we kill manually or until User B is online
What i am asking for , are :
Can we create something like a callback on redis pub/sub ? If the subscriber doesn't accept the message, due to off line, or something else , we close the connection. Is this possible ?
In Node.Js i know i can use timeout function, that will close the subscribe or emit any event if on certain time, there was no message received, how to do this on golang ? I need to inform the User A if User B is active or offline, so he can wait for a another time to create request.
If nothing can, what is your suggestion for me to do this ?
I hope my question , understandable, and can answered well.
*Some code maybe, missing variable.
** I'm using this library for golang redis : go-redis
1) There are no callbacks in Redis.
2) The usual way to implement a timeout in Go is to use channels and select - where one is a channel where you do the blocking and another channel receives a message on timeout. Examples of that can be found here and here for the docs
Now for (3), you have some options on methods. The first is to use a list, pushing from one side (publishing) and popping from another (subscribing). For the receiver you wild use BRPOP of BLPOP - blocking pop from right or left respectively. You can combine the two to have persistent messaging.
Now part of PUBSUB also depends on what you are publishing to. If you are publishing to a channel that would have a subscriber if and only if there is a user connected to receive it (and thus one and only one subscriber to that channel), you can check the response from your publish command. It will tell you how many clients it was published to. If the channel is only subscribed to by an online receiver you would get a '1' back, and a '0' if the user was offline.
A third example is to store the messages in a sorted set, with the timestamp as the score. This would allow the receiver to connect and get messages from the last time it was connected - but that assumes some persistence of that somewhere - usually the client. You would also need some cleanup activity on the sorted sets.
Some other things to consider in this scenario is whether you eventually use replication, in which case you have to explicitly account for failovers - though really in the scenario you describe you'd want to account for disconnects and reconnects. There are specific examples of this at my post on reliable PUBSUB.
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
subscribe := rdb.Subscribe(ctx, "hello")
subscriptions := subscribe.ChannelWithSubscriptions(ctx, 1)
go func() {
var sentCount = 0
for {
rdb.Publish(ctx,"hello",time.Now().UnixNano())
sentCount++
if sentCount >300{
break
}
}
}()
for {
select {
case sub:=<-subscriptions:
fmt.Println(sub)
}
}
}

How to keep connection alive in GO's websocket

I use code.google.com/p/go.net/websocket in server, so client can get notification from server.
however, It seems after client connected to server, if there is no any data tranfer between client and server, server will return EOF error at websocket.JSON.Receive(), it looks like a timeout mechanism.
And I have search in Google, it seems websocket protocol has a ping-pong heartbeat to maintain the connection, I want to ask whether code.google.com/p/go.net/websocket support this ping protocol or not?
What should I do if I want keep connection between client and server alive?
Here's working drop-in solution for gorilla/websocket package.
func keepAlive(c *websocket.Conn, timeout time.Duration) {
lastResponse := time.Now()
c.SetPongHandler(func(msg string) error {
lastResponse = time.Now()
return nil
})
go func() {
for {
err := c.WriteMessage(websocket.PingMessage, []byte("keepalive"))
if err != nil {
return
}
time.Sleep(timeout/2)
if(time.Since(lastResponse) > timeout) {
c.Close()
return
}
}
}()
}
As recently as 2013, the go.net websocket library does not support (automatic) keep-alive messages. You have two options:
Implement an "application level" keep-alive by periodically having your application send a message down the pipe (either direction should work), that is ignored by the other side.
Move to a different websocket library that does support keep-alives (like this one) Edit: it looks like that library has been superseded by Gorilla websockets.

Resources