I have two projects on Golang server and client.
Problem is when I send controll message from server, i can't get it by type on client side.
Few server code example:
send PingMessage:
ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
ws.WriteMessage(websocket.PingMessage, new_msg)
send CloseMessage:
ws.WriteControl(websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseNormalClosure, "socket close"),
time.Now().Add(3 * time.Second))
client side:
for{
t, socketMsg, err := ws.ReadMessage()
if websocket.IsUnexpectedCloseError(err) {
webSock.keepLive()
}
switch t {
case websocket.CloseNormalClosure:
webSock.keepLive()
case websocket.PingMessage:
log.Warn("get ping!!!")
case websocket.TextMessage:
SocketChannel <- socketMsg
}
}
for example CloseNormalClosure message i can get only with:
if websocket.IsCloseError(err, websocket.CloseNormalClosure){
log.Warn("CloseNormalClosure message")
}
But PingMessage, I can't get by type:
case websocket.PingMessage:
log.Warn("get ping!!!")
Could you help me, please, what Im doing wrong ?
The documentation says:
Connections handle received close messages by calling the handler function set with the SetCloseHandler method and by returning a *CloseError from the NextReader, ReadMessage or the message Read method. The default close handler sends a close message to the peer.
Connections handle received ping messages by calling the handler function set with the SetPingHandler method. The default ping handler sends a pong message to the peer.
Connections handle received pong messages by calling the handler function set with the SetPongHandler method. The default pong handler does nothing. If an application sends ping messages, then the application should set a pong handler to receive the corresponding pong.
Write the code above as:
ws.SetPingHandler(func(s string) error {
log.Warn("get ping!!!")
return nil
})
for {
t, socketMsg, err := ws.ReadMessage()
switch {
case websocket.IsCloseError(websocket.CloseNormalClosure):
webSock.keepLive()
case websocket.IsUnexpectedCloseError(err):
webSock.keepLive()
case t == websocket.TextMessage:
SocketChannel <- socketMsg
}
}
Most applications break out of the receive loop on any error. A more typical approach is to write the code above as:
for {
t, socketMsg, err := ws.ReadMessage()
if err != nil {
break
}
SocketChannel <- socketMsg
}
Related
I am writing wrapper on top of nats client in golang, I want to take handler function which can be invoked from consumer once I get the message from nats server.
I want to hold custom subscribe method until it receives the message from nats.
Publish:
func (busConfig BusConfig) Publish(service string, data []byte) error {
pubErr := conn.Publish(service, data)
if pubErr != nil {
return pubErr
}
return nil
}
Subscribe:
func (busConfig BusConfig) Subscribe(subject string, handler func(msg []byte)) {
fmt.Println("Subscrbing on : ", subject)
//wg := sync.WaitGroup{}
//wg.Add(1)
subscription, err := conn.Subscribe(subject, func(msg *nats.Msg) {
go func() {
handler(msg.Data)
}()
//wg.Done()
})
if err != nil {
fmt.Println("Subscriber error : ", err)
}
//wg.Wait()
defer subscription.Unsubscribe()
}
test case:
func TestLifeCycleEvent(t *testing.T) {
busClient := GetBusClient()
busClient.Subscribe(SUBJECT, func(input []byte) {
fmt.Println("Life cycle event received :", string(input))
})
busClient.Publish(SUBJECT, []byte("complete notification"))
}
I am seeing message is published but not subscribed, I tried to hold subscribe method using waitgroup but I think this is not the correct solution.
You don't see the message being delivered because Subscribe is an async method that spawns a goroutine to handle the incoming messages and call the callback.
Straight after calling busClient.Publish() your application exits. It does not wait for anything to happen inside Subscribe().
When you use nats.Subscribe(), you usually have a long-running application that exits in specific conditions (like receiving a shutdown signal). WaitGroup can work here, but probably not for real applications, just for tests.
You should also call Flush() method on NATS connection to ensure all buffered messages have been sent before exiting the program.
If you want a synchronous method, you can use nats.SubscribeSync()
Check out examples here: https://natsbyexample.com/examples/messaging/pub-sub/go
For my understanding, I think in NATs we need to respond to the message even if we are not providing the reply address, so it can respond to the message.
func (busConfig BusConfig) Subscribe(subject string, handler func(msg []byte)) {
subscription, err := conn.Subscribe(subject, func(msg *nats.Msg) {
go func() {
handler(msg.Data)
msg.Respond(nil)
}()
})
}
In go grpc service I have a receiver(publisher) event loop, and publisher can detect that it wants sender to stop. But channel principles says that we should not close channels on receiver side, only on sender side. How should it be threated?
Situation is like following. Imagine a chat. 1st client - subscriber - receives message, and its streaming cannot be done without goroutine due to grpc limitations. And 2nd client - publisher is sending a message to chat, so its another goroutine. You have to pass a message from publisher to subscriber receiving client, ONLY if subscriber not closed its connection (forces closing a channel from receiver side)
The problem in code:
//1st client goroutine - subscriber
func (s *GRPCServer) WatchMessageServer(req *WatchMessageRequest, stream ExampleService_WatchMessageServer) error {
ch := s.NewClientChannel()
// natively blocks goroutine with send to its stream, until send gets an error
for {
msg, ok := <-ch
if !ok {
return nil
}
err := stream.Send(msg) // if this fails - we need to close ch from receiver side to "propagate" closing signal
if err != nil {
return err
}
}
}
//2nd client goroutine - publisher
func (s *GRPCServer) SendMessage(ctx context.Context, req *SendMessageRequest) (*emptypb.Empty, error) {
for i := range s.clientChannels {
s.clientChannels[i] <- req
// no way other than panic, to know when to remove channel from list. or need to make a wrapper with recover..
}
return nil
}
I've initially got a clue by searhing, and solution idea was provided in an answer here thanks to that answer.
Providing streaming solution sample code, i guess its an implementation for a generic pub-sub problem:
//1st client goroutine - subscriber
func (s *GRPCServer) WatchMessageServer(req *WatchMessageRequest, stream ExampleService_WatchMessageServer) error {
s.AddClientToBroadcastList(stream)
select {
case <-stream.Context().Done(): // stackoverflow promised that it would signal when client closes stream
return stream.Context().Err() // stream will be closed immediately after return
case <-s.ctx.Done(): // program shutdown
return s.ctx.Err()
}
}
//2nd client goroutine - publisher
func (s *GRPCServer) SendMessage(ctx context.Context, req *SendMessageRequest) (*emptypb.Empty, error) {
for i := range s.clientStreams {
err := s.clientStreams.Send(req)
if err != nil {
s.RemoveClientFromBroadcastList(s.clientStreams[i])
}
}
return nil
}
Using the following proto buffer code :
syntax = "proto3";
package pb;
message SimpleRequest {
int64 number = 1;
}
message SimpleResponse {
int64 doubled = 1;
}
// All the calls in this serivce preform the action of doubling a number.
// The streams will continuously send the next double, eg. 1, 2, 4, 8, 16.
service Test {
// This RPC streams from the server only.
rpc Downstream(SimpleRequest) returns (stream SimpleResponse);
}
I'm able to successfully open a stream, and continuously get the next doubled number from the server.
My go code for running this looks like :
ctxDownstream, cancel := context.WithCancel(ctx)
downstream, err := testClient.Downstream(ctxDownstream, &pb.SimpleRequest{Number: 1})
for {
responseDownstream, err := downstream.Recv()
if err != io.EOF {
println(fmt.Sprintf("downstream response: %d, error: %v", responseDownstream.Doubled, err))
if responseDownstream.Doubled >= 32 {
break
}
}
}
cancel() // !!This is not a graceful shutdown
println(fmt.Sprintf("%v", downstream.Trailer()))
The problem I'm having is using a context cancellation means my downstream.Trailer() response is empty. Is there a way to gracefully close this connection from the client side and receive downstream.Trailer().
Note: if I close the downstream connection from the server side, my trailers are populated. But I have no way of instructing my server side to close this particular stream. So there must be a way to gracefully close a stream client side.
Thanks.
As requested some server code :
func (b *binding) Downstream(req *pb.SimpleRequest, stream pb.Test_DownstreamServer) error {
request := req
r := make(chan *pb.SimpleResponse)
e := make(chan error)
ticker := time.NewTicker(200 * time.Millisecond)
defer func() { ticker.Stop(); close(r); close(e) }()
go func() {
defer func() { recover() }()
for {
select {
case <-ticker.C:
response, err := b.Endpoint(stream.Context(), request)
if err != nil {
e <- err
}
r <- response
}
}
}()
for {
select {
case err := <-e:
return err
case response := <-r:
if err := stream.Send(response); err != nil {
return err
}
request.Number = response.Doubled
case <-stream.Context().Done():
return nil
}
}
}
You will still need to populate the trailer with some information. I use the grpc.StreamServerInterceptor to do this.
According to the grpc go documentation
Trailer returns the trailer metadata from the server, if there is any.
It must only be called after stream.CloseAndRecv has returned, or
stream.Recv has returned a non-nil error (including io.EOF).
So if you want to read the trailer in client try something like this
ctxDownstream, cancel := context.WithCancel(ctx)
defer cancel()
for {
...
// on error or EOF
break;
}
println(fmt.Sprintf("%v", downstream.Trailer()))
Break from the infinate loop when there is a error and print the trailer. cancel will be called at the end of the function as it is deferred.
I can't find a reference that explains it clearly, but this doesn't appear to be possible.
On the wire, grpc-status is followed by the trailer metadata when the call completes normally (i.e. the server exits the call).
When the client cancels the call, neither of these are sent.
Seems that gRPC treats call cancellation as a quick abort of the rpc, not much different than the socket being dropped.
Adding a "cancel message" via request streaming works; the server can pick this up and cancel the stream from its end and trailers will still get sent:
message SimpleRequest {
oneof RequestType {
int64 number = 1;
bool cancel = 2;
}
}
....
rpc Downstream(stream SimpleRequest) returns (stream SimpleResponse);
Although this does add a bit of complication to the code.
I am using grpc go
i have an rpc which looks roughly like this
196 service MyService {
197 // Operation 1
198 rpc Operation1(OperationRequest) returns (OperationResponse) {
199 option (google.api.http) = {
200 post: "/apiver/myser/oper1"
201 body: "*"
202 };
203 }
Client connects by using grpc.Dial() method
When a client connects, the server does some book keeping. when the client disconnects, the bookkeeping needs to be removed.
is there any callback that can be registered which can be used to know that client has closed the session.
Based on your code, it's an unary rpc call, the client connect to server for only one time, send a request and get a response. The client will wait for the response until timeout.
In server side streaming, you can get the client disconnect from
<-grpc.ServerStream.Context.Done()
signal.
With that above, you can implement your own channel in a go routine to build your logic. Use select statement as:
select {
case <-srv.Context().Done():
return
case res := <-<YOUR OWN CHANNEL, WITH RECEIVED RESQUEST OR YOUR RESPONSE>
....
}
I provide some detailed code here
In client streaming, besides the above signal, you can check whether the server can receive the msg:
req, err := grpc.ServerStream.Recv()
if err == io.EOF {
break
} else if err != nil {
return err
}
Assuming that the server is implemented in go, there's an API on the *grpc.ClientConn that reports state changes in the connection.
func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool
https://godoc.org/google.golang.org/grpc#ClientConn.WaitForStateChange
These are the docs on each of the connectivity.State
https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
If you need to expose a channel that you can listen to for the client closing the connection then you could do something like this:
func connectionOnState(ctx context.Context, conn *grpc.ClientConn, states ...connectivity.State) <-chan struct{} {
done := make(chan struct{})
go func() {
// any return from this func will close the channel
defer close(done)
// continue checking for state change
// until one of break states is found
for {
change := conn.WaitForStateChange(ctx, conn.GetState())
if !change {
// ctx is done, return
// something upstream is cancelling
return
}
currentState := conn.GetState()
for _, s := range states {
if currentState == s {
// matches one of the states passed
// return, closing the done channel
return
}
}
}
}()
return done
}
If you only want to consider connections that are shutting down or shutdown, then you could call it like so:
// any receives from shutdownCh will mean the state Shutdown
shutdownCh := connectionOnState(ctx, conn, connectivity.Shutdown)
as the github issue:link
you can do like this
err = stream.Context().Err()
if err != nil {
break
}
How is it possible to close a websocket connection and pass it a message / code?
The docs only define func (ws *Conn) Close() error without any arguments
I would like to receive the event from JavaScript like this:
websocket.onclose = function(event) {
console.log(event);
};
I am using golang.org/x/net/websocket
Send a close message before closing the connection:
cm := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "add your message here")
if err := c.WriteMessage(websocket.CloseMessage, cm); err != nil {
// handle error
}
c.Close()
It is not possible to specify the close message with the golang.org/x/net/websocket package.