RPC from both client and server in Go - go

Is it actually possible to do RPC calls from a server to a client with the net/rpc package in Go? If no, is there a better solution out there?

I am currently using thrift (thrift4go) for server->client and client->server RPC functionality. By default, thrift does only client->server calls just like net/rpc. As I also required server->client communication, I did some research and found bidi-thrift. Bidi-thrift explains how to connect a java server + java client to have bidirectional thrift communication.
What bidi-thrift does, and it's limitations.
A TCP connection has an incomming and outgoing communication line (RC and TX). The idea of bidi-thrift is to split RS and TX and provide these to a server(processor) and client(remote) on both client-application and server-application. I found this to be hard to do in Go. Also, this way there is no "response" possible (the response line is in use). Therefore, all methods in the service's must be "oneway void". (fire and forget, call gives no result).
The solution
I changed the idea of bidi-thrift and made the client open two connections to the server, A and B. The first connection(A) is used to perform client -> server communication (where client makes the calls, as usual). The second connection(B) is 'hijacked', and connected to a server(processor) on the client, while it is connected to a client(remote) on the server. I've got this working with a Go server and a Java client. It works very well. It's fast and reliable (just like normal thrift is).
Some sources.. The B connection (server->client) is set up like this:
Go server
// factories
framedTransportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
// create socket listener
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9091")
if err != nil {
log.Print("Error resolving address: ", err.Error(), "\n")
return
}
serverTransport, err := thrift.NewTServerSocketAddr(addr)
if err != nil {
log.Print("Error creating server socket: ", err.Error(), "\n")
return
}
// Start the server to listen for connections
log.Print("Starting the server for B communication (server->client) on ", addr, "\n")
err = serverTransport.Listen()
if err != nil {
log.Print("Error during B server: ", err.Error(), "\n")
return //err
}
// Accept new connections and handle those
for {
transport, err := serverTransport.Accept()
if err != nil {
return //err
}
if transport != nil {
// Each transport is handled in a goroutine so the server is availiable again.
go func() {
useTransport := framedTransportFactory.GetTransport(transport)
client := worldclient.NewWorldClientClientFactory(useTransport, protocolFactory)
// Thats it!
// Lets do something with the connction
result, err := client.Hello()
if err != nil {
log.Printf("Errror when calling Hello on client: %s\n", err)
}
// client.CallSomething()
}()
}
}
Java client
// preparations for B connection
TTransportFactory transportFactory = new TTransportFactory();
TProtocolFactory protocolFactory = new TBinaryProtocol.Factory();
YourServiceProcessor processor = new YourService.Processor<YourServiceProcessor>(new YourServiceProcessor(this));
/* Create thrift connection for B calls (server -> client) */
try {
// create the transport
final TTransport transport = new TSocket("127.0.0.1", 9091);
// open the transport
transport.open();
// add framing to the transport layer
final TTransport framedTransport = new TFramedTransport(transportFactory.getTransport(transport));
// connect framed transports to protocols
final TProtocol protocol = protocolFactory.getProtocol(framedTransport);
// let the processor handle the requests in new Thread
new Thread() {
public void run() {
try {
while (processor.process(protocol, protocol)) {}
} catch (TException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}.start();
} catch(Exception e) {
e.printStackTrace();
}

I came across rpc2 which implements it. An example:
Server.go
// server.go
package main
import (
"net"
"github.com/cenkalti/rpc2"
"fmt"
)
type Args struct{ A, B int }
type Reply int
func main(){
srv := rpc2.NewServer()
srv.Handle("add", func(client *rpc2.Client, args *Args, reply *Reply) error{
// Reversed call (server to client)
var rep Reply
client.Call("mult", Args{2, 3}, &rep)
fmt.Println("mult result:", rep)
*reply = Reply(args.A + args.B)
return nil
})
lis, _ := net.Listen("tcp", "127.0.0.1:5000")
srv.Accept(lis)
}
Client.go
// client.go
package main
import (
"fmt"
"github.com/cenkalti/rpc2"
"net"
)
type Args struct{ A, B int }
type Reply int
func main(){
conn, _ := net.Dial("tcp", "127.0.0.1:5000")
clt := rpc2.NewClient(conn)
clt.Handle("mult", func(client *rpc2.Client, args *Args, reply *Reply) error {
*reply = Reply(args.A * args.B)
return nil
})
go clt.Run()
var rep Reply
clt.Call("add", Args{5, 2}, &rep)
fmt.Println("add result:", rep)
}

RPC is a (remote) service. Whenever some computer requests a remote service then it is acting as a client asking the server to provide the service. Within this "definition" the concept of a server calling client RPC has no well defined meaning.

Related

Memcached Ping() doesn't return an error on an invalid server

I use memcache for caching and the client I use is https://github.com/bradfitz/gomemcache. When I tried initiate new client with dummy/invalid server address and then pinging to it, it return no error.
package main
import (
"fmt"
m "github.com/bradfitz/gomemcache"
)
func main() {
o := m.New("dummy_adress")
fmt.Println(o.Ping()) // return no error
}
I think it suppose to return error as the server is invalid. What do I miss?
It looks like the New() call ignores the return value for SetServers:
func New(server ...string) *Client {
ss := new(ServerList)
ss.SetServers(server...)
return NewFromSelector(ss)
}
The SetServers() function will only set the server list to valid servers (in
your case: no servers) and the Ping() funtion will only ping servers that are
set, and since there are no servers set it doesn't really do anything.
This is arguably a feature; if you have 4 servers and one is down then that's
not really an issue. Even with just 1 server memcache is generally optional.
You can duplicate the New() logic with an error check:
ss := new(memcache.ServerList)
err := ss.SetServers("example.localhost:11211")
if err != nil {
panic(err)
}
c := memcache.NewFromSelector(ss)
err = c.Ping()
if err != nil {
panic(err)
}
Which gives:
panic: dial tcp 127.0.0.1:11211: connect: connection refused

rpc error: code = Unimplemented desc = RPC method not implemented

I have been trying to create a grpc client in Go and I have followed the correct instructions as shown in the official grpc site. When I start my grpc server written in node.js, the connection works well but upon compiling the protocol buffer in Go and creating a client interface with the correct grpc client configurations, I run into an error.
Here is my what I have in my identity.pb.go.
type IdentityServiceClient interface {
CreateUser(ctx context.Context, in *GoogleIdToken, opts ...grpc.CallOption) (error, *UserInfo)
}
type simpleServerClient struct {
connection *grpc.ClientConn
}
func NewSimpleServerClient(connection *grpc.ClientConn) IdentityServiceClient {
return &simpleServerClient{connection}
}
func (simpleClient *simpleServerClient) CreateUser(ctx context.Context, in *GoogleIdToken, opts ...grpc.CallOption) (error, *UserInfo) {
out := new(UserInfo)
err := simpleClient.connection.Invoke(ctx, "/protobuf.IdentityService/CreateUser", in, out, opts...)
if err != nil {
return err, nil
}
return nil, out
}
here is the identity.proto
syntax="proto3";
package protobuf;
service IdentityService {
rpc CreateUser (GoogleIdToken) returns (UserInfo) {}
}
message GoogleIdToken {
string token = 1;
}
message UserInfo {
string name = 1;
string email = 2;
message Profile {
string imageUrl = 1;
string lastUpdated = 2;
};
Profile profile = 3;
string token = 4;
}
here is my main.go
import pb "github.com/Duncanian/iam-gateway/server/protobuf"
func grpcConnection() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to start gRPC connection: %v", err)
}
defer conn.Close()
client := pb.NewSimpleServerClient(conn)
err, _ = client.CreateUser(context.Background(), &pb.GoogleIdToken{Token: "tevgvybububvvg"})
if err != nil {
log.Fatalf("Failed to create user: %v", err)
}
log.Println("Created user!")
}
I expect the output of passing a correct google auth token to get me the correct user details which are
name: user,
email: user.email#user.com,
profile: {
imageUrl: myimageUrl,
lastUpdated: mylastUpdatedTime,
},
token,
but got
rpc error: code = Unimplemented desc = RPC method not implemented /protobuf.IdentityService/CreateUser
Here are my github repos:
Go grpc client &
Node JS grpc server
I had the same problem.
Here is my solution:
After compiling .proto I created 2 files: client.go and server.go.
In client.go I implemented my methods (rpc in terms of protobuffers) and had a main function.
In server.go I defined a server struct server with one field: Unimplemented*ServiceName*Server. After that I also implemented the mentioned above methods, but those had a receiver type: func (s *server) Foo(ctx context.Context, *other params*)
That worked for me, hopefully it will help you!
The error indicates that the /protobuf.IdentityService/CreateUser method is not registered at the server side. And I didn't see any service registration code in your linked server code. Please take a look at the node.js guide here.
Using GUI of grpc try to send your protos to your both servers and check for upcoming errors/correct endpoints.
In my case java proto had a package inside which was added to the endpoint.
Had package com.example.grpc;
instead of
option java_package = "com.example.grpc";

grpc go : how to know in server side, when client closes the connection

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
}

Gorilla websocket error: close 1007 Illegal UTF-8 Sequence

I'm trying to implement a websocket proxy server for GlassFish. If I try to connect more than one client I'm getting error:
ReadMessage Failed: websocket: close 1007 Illegal UTF-8 Sequence.
I'm sure the GlassFish server sending right data, because the same server works properly with another proxy server implemented with node.js.
func GlassFishHandler(conn *websocket.Conn){
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(1000 * time.Second))
conn.SetWriteDeadline(time.Now().Add(1000 * time.Second))
fmt.Println("WS-GOLANG PROXY SERVER: Connected to GlassFish")
for {
messageType, reader, err := conn.NextReader()
if err != nil {
fmt.Println("ReadMessage Failed: ", err) // <- error here
} else {
message, err := ioutil.ReadAll(reader)
if (err == nil && messageType == websocket.TextMessage){
var dat map[string]interface{}
if err := json.Unmarshal(message, &dat); err != nil {
panic(err)
}
// get client destination id
clientId := dat["target"].(string)
fmt.Println("Msg from GlassFish for Client: ", dat);
// pass through
clients[clientId].WriteMessage(websocket.TextMessage, message)
}
}
}
}
Summing up my comments as an answer:
When you are writing to the client, you are taking the clientId from the GlassFish message, fetching the client from a map, and then writing to it - basically clients[clientId].WriteMessage(...).
While your map access can be thread safe, writing is not, as this can be seen as:
// map access - can be safe if you're using a concurrent map
client := clients[clientId]
// writing to a client, not protected at all
client.WriteMessage(...)
So what's probably happening is that two separate goroutines are writing to the same client at the same time. You should protect your client from it by adding a mutex in the WriteMessage method implementation.
BTW actually instead of protecting this method with a mutex, a better, more "go-ish" approach would be to use a channel to write the message, and a goroutine per client that consumes from the channel and writes to the actual socket.
So in the client struct I'd do something like this:
type message struct {
msgtype string
msg string
}
type client struct {
...
msgqueue chan *message
}
func (c *client)WriteMessage(messageType, messageText string) {
// I'm simplifying here, but you get the idea
c.msgqueue <- &message{msgtype: messageType, msg: messageText}
}
func (c *client)writeLoop() {
go func() {
for msg := ragne c.msgqueue {
c.actuallyWriteMessage(msg)
}
}()
}
and when creating a new client instance, just launch the write loop

Sending a message to set of hosts with acknowledgements

Function is like:
func Message(worker_ID int, message string, args *Args , reply *int) chan bool {
}
This function resides at host which is called by client when it want to send the message to hosts, hosts are located at different place, so both IP and port required to send message right? which mechanism can be helpful net.dial() or gob or rpc?
If you want something simple then check out net/rpc which wraps gob and networking into a remote procedure call framework which should do what you want.
Server
From the docs a server running over HTTP
type Args struct {
A, B int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
Client
At this point, clients can see a service "Arith" with method "Arith.Multiply". To invoke one, dial the server then make a call. You can also make asynchronous calls where the result comes back in a channel.
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
log.Fatal("dialing:", err)
}
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
A slight oddity of the framework is that each remote call can have only one input argument and one output argument which means that you need to wrap all your arguments in a struct.
//server.go will provide the interface for communicating and handling hosts
// workerDead(message string), send the message and wait for ack, if no ack means worker dead
package
main
import(
"fmt"
"io"
"net"
"net/http"
"net/rpc"
"path"
"os"
)
type Flag int
type Args struct{
message string
}
func main() {
flag := new(Flag)
rpc.Register(flag)
rpc.HandleHTTP()
err := http.ListenAndServe(":1234", nil) //ListenAndServe starts an HTTP server with a given address and handler.
//The handler is usually nil, which means to use DefaultServeMux.
if err != nil {
fmt.Println(err.Error())
}
}
//Worker counts the number of hosts
func workerCount() int
{
return db.runCommand( { count: 'id' } ) //mongodb command to count the id
}
// Returns an array of the distinct values of field id from all documents in the workers collection
//In mongodb document is analogous to rdbms table and collection is record
func Worker(worker int) []string{
return db.runCommand ({ distinct: 'workers', key: 'id' } ) //mongodb command to get the array of list of
//workers for column id
}
func Message(worker int, message string, args *Args , reply *int) chan bool {
server, err :=rpc.Dial("tcp","192.168.23.12") //Serve Dials here to send message to host, IP address here is of host
if(err!=nil){
log.Fatal("Dialing", err)
}
var reply bool
args:=Args{message};
err = server.Call(args,&reply);
if(err!=nil){
log.Fatal("Dialing", err)
replaceWorker(); // No reply from worker then call to replace worker
}
fmt.Println("Reply from host", reply);
}
return nil
}
//Replace dead worker of particular project with fresh worker
func replaceWorker(worker_id int,project_id int)
{
db.workers.update( //this query updates workers collection with id=worker_id(id of dead worker)
{ _id: worker_id, _project_id: project_id },
{
//$set: { ' ': 'Warner' },
}
)
}

Resources