關於如何使用golang進行json-rpc的調用,網上雖然有很多的帖子,但都僅僅是golang程序間通信,沒有牽涉到跨語言調用的問題。在使用golang開發服務端程序的時候,不可避免的要與其他語言的程序進行交互,特別是json-rpc這的協議,本身就應該是用在不同的平台間的調用上的(因為golang程序間的交流已經有了封閉的用gob編碼解碼的rpc包了,我們自然而言的會想到用json-rpc來提供對其他語言的支持)。本文就來詳細探究一下如何實現golang與java之間的json-rpc調用。轉載請注明出處:http://www.cnblogs.com/geomantic/p/4751859.html
- 首先,實現一個基於socket的java調用golang的樣例(這個方法不需要第三方golang庫,因為golang本身就有jsonrpc的支持庫,但是僅能通過tcp協議通信,並且有一些格式上的局限,直接與其他的語言交互會有些障礙。如果要通過http協議通信的話,必須自己寫一個或者用第三方的庫,后面會有介紹)
package rpcz // first we create a simple golang rpc server based on socket import ( "fmt" "net" "net/rpc" "net/rpc/jsonrpc" ) type Counter struct { Sum int } func (this *Counter) Add(i int, r *int) error { this.Sum += i *r = this.Sum fmt.Printf("i: %v", i) return nil } func NewJsonRpcSocketServer() { rpc.Register(new(Counter)) l, err := net.Listen("tcp", ":3333") if err != nil { fmt.Printf("Listener tcp err: %s", err) return } for { fmt.Println("wating...") conn, err := l.Accept() if err != nil { fmt.Sprintf("accept connection err: %s\n", conn) } go jsonrpc.ServeConn(conn) } } func NewJsonRpcSocketClient() { conn, err := net.DialTimeout("tcp", "127.0.0.1:3333", 1000*1000*1000*30) if err != nil { fmt.Printf("create client err:%s\n", err) return } defer conn.Close() client := jsonrpc.NewClient(conn) var reply int err = client.Call("Counter.Add", 10, &reply) fmt.Printf("reply: %s, err: %s\n", reply, err) }
上面的NewJsonRpcSocketServer()方法創建了一個基於tcp協議的json-rpc服務器,NewJsonRpcSocketClient()方法示范了golang端的簡單調用(注意是基於tcp,而不是http協議!稍后會給出調用"github.com/gorilla/rpc/json"實現的基於http協議的調用方法)。但這不是重點,下面看一下Java客戶端的調用,可以看出來,這也是一個基於socket的通信:
package cn; import com.googlecode.jsonrpc4j.JsonRpcClient; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.*; /** * Created by geomantic on 15/8/21. */ public class SocketClient { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 3333); JsonRpcClient client = new JsonRpcClient(); InputStream ips = socket.getInputStream(); OutputStream ops = socket.getOutputStream(); int reply = client.invokeAndReadResponse("Counter.Add", new Object[]{1001}, int.class, ops, ips); System.out.println("reply: " + reply); } catch (IOException e) { e.printStackTrace(); } catch (Throwable throwable) { throwable.printStackTrace(); } } }
上面的java代碼初步示范了如何異步調用golang服務端的兩個很重要的約束:
1. golang服務端的方法名注冊和調用保持一致,格式為: 結構體名.方法名, 如上面的Counter.Add。
2. 能夠注冊的方法必須滿足指定的函數簽名:
func (t *T) MethodName(argType T1, replyType *T2) error 。
入參有且只有兩個,第一個是被調用函數的輸入參數,第二個是被調用函數的輸出參數。 返回值只有一個error類型。這三個參數一個都不能變。
但是基於tcp的調用,對於用慣了http協議的人會感覺寫起來很蛋疼,可能更多的人還是習慣用http來解析。但是由於解決的方法有比較瑣碎的細節處理,而且牽涉到一些第三方庫的代碼修改,考慮到篇幅問題准備另起一篇,在下一篇中示范一下用http協議通信實現的rpc實現。