RPC(Remote Procedure Call)—遠程過程調用,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通訊程序之間攜帶信息數據。在OSI網絡通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。git
RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,而後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息到達爲止。當一個調用信息到達,服務器得到進程參數,計算結果,發送答覆信息,而後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,得到進程結果,而後調用執行繼續進行。web
有多種 RPC模式和執行。最初由 Sun 公司提出。IETF ONC 憲章從新修訂了 Sun 版本,使得 ONC RPC 協議成爲 IETF 標準協議。如今使用最廣泛的模式和執行是開放式軟件基礎的分佈式計算環境(DCE)。json
我的的理解:不用管什麼底層網絡技術的協議,是一種經過網絡從計算機程序上請求服務,通俗一點,咱們寫代碼,要在一個地方,好比安卓,就須要在一個工程裏面,才能夠調用到其餘的程序代碼執行的過程。Go語言提供RPC支持使得開發網絡分佈式多程序在內的應用程序更加的easy
數組
HTTP、TCP、JSPNRPC
,可是在Go
中RPC
是獨一無二的,它採用了GoLang Gob
編碼,只能支持Go語言!package main
import (
"fmt"
"net/rpc"
"net/http"
"errors"
)
func main() {
rpcDemo()
}
type Arith int
func rpcDemo() {
arith:=new(Arith)
//arith=== 0xc04204e090
fmt.Println("arith===",arith)
rpc.Register(arith)
//HandleHTTP將RPC消息的HTTP處理程序註冊到Debug服務器
//DEFAUTUPCPATH和Debug調試路徑上的調試處理程序。
//仍然須要調用http.Services(),一般是在GO語句中。
rpc.HandleHTTP()
err:=http.ListenAndServe(":1234",nil)
if err != nil {
fmt.Println("err=====",err.Error())
}
}
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
//函數必須是導出的(首字母大寫)
//必須有兩個導出類型的參數,
//第一個參數是接收的參數,第二個參數是返回給客戶端的參數,第二個參數必須是指針類型的
//函數還要有一個返回值error
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
fmt.Println("這個方法執行了啊---嘿嘿--- Multiply ",reply)
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
fmt.Println("這個方法執行了啊---嘿嘿--- Divide quo==",quo)
return nil
}
複製代碼
error
func (t *T) MethodName(argType T1, replyType *T2) error
複製代碼
T、T1和T2類型必須能被encoding/gob
包編解碼。bash
客戶端的代碼服務器
package main
import (
"log"
"fmt"
"os"
"net/rpc"
"strconv"
)
type ArgsTwo struct {
A, B int
}
type QuotientTwo struct {
Quo, Rem int
}
func main() {
// 若是什麼都不輸入的話 ,就是如下的這個值
//os***************** [C:\Users\win7\AppData\Local\Temp\go-build669605574\command-
//line-arguments\_obj\exe\GoRPCWeb.exe 127.0.0.1] **********************
fmt.Println("os*****************",os.Args,"**********************")
if len(os.Args) != 4 { // todo 第二個地址是 咱們本地的地址
fmt.Println("老子要退出了哦 傻逼 一號start--------》》》", os.Args[0], "《《《---------------server end")
os.Exit(1)
}else{
fmt.Println("長度是多少 "+strconv.Itoa( len(os.Args))+"纔是準確的長度 哦---》")
}
//獲取輸入的地址是獲取輸入得 os 數據的 第一個位置的值
serverAddress := os.Args[1]
fmt.Println("severAddress==",serverAddress)
// //DelayHTTP在指定的網絡地址鏈接到HTTP RPC服務器
///在默認HTTP RPC路徑上監聽。
client, err := rpc.DialHTTP("tcp", serverAddress)
if err != nil {
log.Fatal("發生錯誤了 在這裏地方 DialHTTP", err)
}
i1,_:=strconv.Atoi( os.Args[2])
i2,_:=strconv.Atoi( os.Args[3])
args := ArgsTwo{i1, i2}
var reply int
//調用調用命名函數,等待它完成,並返回其錯誤狀態。
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("Call Multiply 發生錯誤了哦 arith error:", err)
}
fmt.Printf("Arith 乘法: %d*%d=%d\n", args.A, args.B, reply)
var quot QuotientTwo
//調用調用命名函數,等待它完成,並返回其錯誤狀態。
err = client.Call("Arith.Divide", args, ")
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith 除法取整數: %d/%d=%d 餘數 %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
複製代碼
E:\new_code\GoDemo\web_server>go run GoRPCWeb8.go 127.0.0.1:1234 20 3
os***************** [C:\Users\win7\AppData\Local\Temp\go-build011170718\command-
line-arguments\_obj\exe\GoRPCWeb8.exe 127.0.0.1:1234 20 3] *********************
*
長度是多少 4纔是準確的長度 哦---》
severAddress== 127.0.0.1:1234
Arith 乘法: 20*3=60
Arith 除法取整數: 20/3=6 餘數 2
複製代碼
cmd
20*3=60
和除法取整數: 20/3=6
餘數 2
,怎麼作的,客戶端不關心,給到服務端去完成os.Args[0]
=[C:\Users\win7\AppData\Local\Temp\go-build011170718\command- line-arguments\_obj\exe\GoRPCWeb8.exe 127.0.0.1:1234 20 3]
if len(os.Args) != 4 { // todo 第二個地址是 咱們本地的地址
fmt.Println("老子要退出了哦 傻逼 一號start--------》》》", os.Args[0], "《《《---------------server end")
os.Exit(1)
}else{
fmt.Println("長度是多少 "+strconv.Itoa( len(os.Args))+"纔是準確的長度 哦---》")
}
複製代碼
package main
import (
"fmt"
"net/rpc"
"net"
"os"
"errors"
)
func init() {
fmt.Println("基於TCP協議實現的RPC,服務端的代碼以下")
}
type Me struct {
A,B int
}
type You struct {
CC,D int
}
type Num int
/*
Go RPC的函數只有符合下面的條件纔可以被遠程訪問,否則會被忽略
1 函數必須是導出的(首字母大寫)
2 必須有兩個導出類型的參數
3 第一個參數是接受的參數,第二個參數是返回給客戶端的參數,第二個參數必須是指正類型的
4 函數還必須有一個返回值error
*/
func (n *Num) M(args *Me,reply *int) error {
*reply=args.A * args.B
return nil
}
func (n *Num) F(args * Me,u *You ) error {
if args.B==0{
return errors.New("輸入不可以爲0 被除數")
}
u.D=args.A/args.B
u.CC=args.A % args.B
return nil
}
func main() {
//內建函數new本質上說跟其它語言中的同名函數功能同樣:new(T)分配了零值填充的T類型的內存空間,而且返回其地址,即一個*T類型的值。用Go的術語說,它返回了一個指針,指向新分配的類型T的零值。有一點很是重要:
//new返回指針。
num:=new(Num)
rpc.Register(num)
//ResolveTCPAddr返回TCP端點的地址。
//網絡必須是TCP網絡名稱。
tcpAddr,err:=net.ResolveTCPAddr("tcp",":1234")
if err != nil {
fmt.Println("錯誤了哦")
os.Exit(1)
}
listener,err:=net.ListenTCP("tcp",tcpAddr)
for {
// todo 須要本身控制鏈接,當有客戶端鏈接上來後,咱們須要把這個鏈接交給rpc 來處理
conn,err:=listener.Accept()
if err != nil {
continue
}
rpc.ServeConn(conn)
}
}
複製代碼
package main
import (
"fmt"
"os"
"net/rpc"
"log"
"strconv"
)
func main() {
fmt.Println("客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go")
if len(os.Args)==4{
fmt.Println("長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip=",os.Args[1],"嘿嘿,加上後面的被除數os.Args[2]=",os.Args[2],"和除數os.Args[3]=",os.Args[3])
//os.Exit(1)
}
// 獲取 ip 地址
service:= os.Args[1]
//鏈接 撥號鏈接到指定的網絡地址的RPC服務器。
client,err:=rpc.Dial("tcp",service)
if err!=nil {
log.Fatal("老子在鏈接Dial的發生了錯誤,我要退出了",err)
}
num1:=os.Args[2]
i1,error1:=strconv.Atoi(num1)
if error1!=nil {
fmt.Println("本身不知道 本身輸入錯誤了啊 請看error :",error1)
os.Exit(1)
}
num2:=os.Args[3]
i2,error2:=strconv.Atoi(num2)
if error2!=nil {
fmt.Println("本身不知道 本身輸入錯誤了啊 請看error :",error2)
os.Exit(1)
}
aa:=AAA{i1,i2}
var reply int
err1:=client.Call("Num.M",aa,&reply)
if err1 != nil{
log.Fatal("我要退出了,由於我在Call的時候發生了 錯誤",err1)
}
fmt.Println("我進行正常結果以下")
fmt.Printf("Num : %d*%d=%d\n",aa.A,aa.B,reply)
var bb BDemo
//調用調用命名函數,等待它完成,並返回其錯誤狀態。
err= client.Call("Num.F",aa,&bb)
if err!=nil {
log.Fatal("我對這個方法發生了過敏的反應 哈哈哈哈 err=====",err)
}
fmt.Printf("Num: %d/%d=%d 餘數 %d\n",aa.A,aa.B,bb.DD,bb.CC)
}
// 定義兩個類,那邊須要操做的類
type AAA struct {
A,B int
}
//記住這裏不可以大寫 兩個連着一塊兒大寫 有點意思
//reading body gob: type mismatch: no fields matched compiling decoder for DDDD
// todo 爲啥 第二個參數 只要是兩個連在一塊兒的DDDD 就會報錯 reading body gob: type mismatch: no fields matched compiling decoder for
type BDemo struct {
DD, CC int
}
複製代碼
E:\new_code\GoDemo\web_server>go run GoTCPRPCWeb10.go 127.0.0.1:1234 20 1
客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go
長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip= 127.0.0.1:1234 嘿嘿,加上後面
的被除數os.Args[2]= 20 和除數os.Args[3]= 1
我進行正常結果以下
Num : 20*1=20
Num: 20/1=0 餘數 0
E:\new_code\GoDemo\web_server>go run GoTCPRPCWeb10.go 127.0.0.1:1234 20 2
客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go
長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip= 127.0.0.1:1234 嘿嘿,加上後面
的被除數os.Args[2]= 20 和除數os.Args[3]= 2
我進行正常結果以下
Num : 20*2=40
Num: 20/2=0 餘數 0
E:\new_code\GoDemo\web_server>go run GoTCPRPCWeb10.go 127.0.0.1:1234 20 3
客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go
長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip= 127.0.0.1:1234 嘿嘿,加上後面
的被除數os.Args[2]= 20 和除數os.Args[3]= 3
我進行正常結果以下
Num : 20*3=60
Num: 20/3=0 餘數 2
複製代碼
BDemo
的時候, 若是定義的DD, CC int
和服務端不同,就會報錯 reading body gob: type mismatch: no fields matched compiling decoder for
,其實發現好多種狀況,也會出現這種錯誤,可是目前不知道爲啥會這樣,後續,等源碼深刻一點,回來看這個問題 todo2018/07/19TCP
方式和上面的HTTP
不一樣的是
//DelayHTTP在指定的網絡地址鏈接到HTTP RPC服務器
///在默認HTTP RPC路徑上監聽。
client, err := rpc.DialHTTP("tcp", serverAddress)
複製代碼
client,err:=rpc.Dial("tcp",service)
複製代碼
JSON RPC
是數據編碼採用了JSON
,而不是gob
編碼,其餘和上面介紹的RPC
概念如出一轍的。網絡
服務端的代碼以下數據結構
package main
import (
"fmt"
"net/rpc"
"net"
"net/rpc/jsonrpc"
)
//使用Go提供的json-rpc 標準包
func init() {
fmt.Println("JSON RPC 採用了JSON,而不是 gob編碼,和RPC概念如出一轍,")
}
type Work struct {
Who,DoWhat string
}
type DemoM string
func (m *DemoM) DoWork(w *Work,whoT *string) error {
*whoT="是誰:"+w.Who+",在作什麼---"+w.DoWhat
return nil
}
func main() {
str:=new(DemoM)
rpc.Register(str)
tcpAddr,err:=net.ResolveTCPAddr("tcp",":8080")
if err!=nil{
fmt.Println("大哥發生錯誤了啊,請看錯誤 ResolveTCPAddr err=",err)
}
listener,err:=net.ListenTCP("tcp",tcpAddr)
if err!=nil {
fmt.Println("發生錯誤了--》err=",err)
}
for {
conn,err:= listener.Accept()
if err!=nil {
continue
}
jsonrpc.ServeConn(conn)
}
}
複製代碼
package main
import (
"fmt"
"os"
"net/rpc/jsonrpc"
"log"
)
func main() {
fmt.Println("這是客戶端,用來啓動,經過命令行來啓動")
fmt.Println("客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go")
if len(os.Args)==4{
fmt.Println("長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip=",os.Args[1],"嘿嘿,加上後面的被除數os.Args[2]=",os.Args[2],"和除數os.Args[3]=",os.Args[3])
//os.Exit(1)
}
service:=os.Args[1]
client,err:=jsonrpc.Dial("tcp",service)
if err != nil {
log.Fatal("Dial 發生了錯誤了哦 錯誤的信息爲 err=",err)
}
send:=Send{os.Args[2],os.Args[3]}
var resive string
err1:=client.Call("DemoM.DoWork",send,&resive)
if err1!=nil {
fmt.Println("shiming call error ")
fmt.Println("Call 的時候發生了錯誤了哦 err=",err1)
}
fmt.Println("收到信息了",resive)
}
// 類能夠不同 可是 Who 和DoWhat 要必須同樣 要否則接收到不到值,等我在詳細的瞭解了 纔去分析下緣由 感受有點矇蔽啊
type Send struct {
Who, DoWhat string
}
複製代碼
E:\new_code\GoDemo\web_server>go run GoJSONRPCWeb11.go 127.0.0.1:8080 shiming g
ongzuo
這是客戶端,用來啓動,經過命令行來啓動
客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go
長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip= 127.0.0.1:8080 嘿嘿,加上後面
的被除數os.Args[2]= shiming 和除數os.Args[3]= gongzuo
收到信息了 是誰:shiming,在作什麼---gongzuo
E:\new_code\GoDemo\web_server>go run GoJSONRPCWeb11.go 127.0.0.1:8080 shiming q
iaodaima
這是客戶端,用來啓動,經過命令行來啓動
客戶端 其餘端 去調用的地方 對應的例子是 GoTCPRPC9.go
長度必須等於4,由於呢,你輸入的確定是一個ip的地址ip= 127.0.0.1:8080 嘿嘿,加上後面
的被除數os.Args[2]= shiming 和除數os.Args[3]= qiaodaima
收到信息了 是誰:shiming,在作什麼---qiaodaima
複製代碼
os.Args
是一個數組 var Args []string
,經過輸入獲取到,而後把這個客戶端輸入的內容傳送到服務端,服務端作些操做,而後在返回給客戶端Go
已經提供了RPC
良好的支持,經過HTTP
TCP
JSONRPC
的實現,能夠很方便開發分佈式的Web
應用,可是我目前還不會,在學習中。遺憾的是Go
沒有提供SOAP RPC
的支持~~~