Go语言下RPC的开发

一、rpc之HelloWorld

Go语言的rpc包的路径为net/rpc:

1、server.go

package main

import (
    "net"
    "net/rpc"
)

type HelloWorldService struct {
}

func (s *HelloWorldService) HelloWorld(request string, response *string) error {
    *response = "hello" + request
    return nil
}

func main() {
    _ = rpc.RegisterName("HelloWorldService", &HelloWorldService{})
    listener, err := net.Listen("tcp", ":8000")
    if err != nil {
        panic("监听端口失败!")
    }
    conn, err := listener.Accept()
    if err != nil {
        panic("建立连接失败!")
    }
    rpc.ServeConn(conn)
}
  • HelloWorld方法需要满足Go语言RPC规则,接收两个参数,第二个参数是指针类型,并且返回一个error类型,同时必须是公开方法
  • HelloWorldService类型的对象注册到RPC服务
  • rpc.register函数调用会将对象类型中满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放置在HelloWorldService服务空间下
  • 建立TCP链接,通过rpc.ServerConn函数在该链接上提供RPC服务

1、client.go

package main

import (
    "fmt"
    "log"
    "net/rpc"
)

func main() {
    client, err := rpc.Dial("tcp", "127.0.0.1:8000")
    if err != nil {
        log.Fatal("dial", err)
    }
    var response string
    err = client.Call("HelloWorldService.HelloWorld", "world", &response)
    if err != nil {
        log.Fatal("caller", err)
    }
    fmt.Println(response)

}
  • 通过rpc.Dial进行RPC拨号服务
  • 通过client.Call调用具体方法
  • 方法中第一个参数是RPC服务名称和方法名称,第二个和第三个参数是方法中传入的实参 

二、基于json实现RPC

上述RPC默认采用的是Go语言特有的gob编码,所以其它语言调用Go语言实现的RPC服务相对困难。比较常见的有基于json实现的RPC服务,所以如果使用json来替换gob将会使其通用性变的更强。在Go语言中可以通过net/rpc/jsonrpc来基于json实现RPC,实现跨语言调用。

1、server.go

package main

import (
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
)

type HelloWorldService struct {
}

func (s *HelloWorldService) HelloWorld(request string, response *string) error {
    *response = "hello" + request
    return nil
}

func main() {
    _ = rpc.RegisterName("HelloWorldService", &HelloWorldService{})
    listener, err := net.Listen("tcp", ":8000")
    if err != nil {
        panic("监听端口失败!")
    }
    // 不断的接收新的请求
    for {
        conn, err := listener.Accept()
        if err != nil {
            panic("建立连接失败!")
        }
        go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) // 启动协程处理请求
    }

}

这与之前相比使用rpc.ServeCodec函数替代了rpc.ServeConn函数,传入符合json编码的参数。

2、client.go

package main

import (
    "fmt"
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
)

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8000")
    if err != nil {
        log.Fatal("dial", err)
    }
    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
    var response string
    err = client.Call("HelloWorldService.HelloWorld", "world", &response)
    if err != nil {
        log.Fatal("caller", err)
    }
    fmt.Println(response)

}

这与之前相比通过net进行拨号建立连接,然后通过NewClientWithCodec函数传入符合json编码的参数。

既然使用的是json进行编、解码,那么就具备一定的通用性,可以使用其它语言来进行调用,比如使用Python客户端进行调用,但是Go的RPC监听的是TCP连接,如果使用Python中的requests包是行不通的,它使用的是HTTP协议,会携带很多比如请求头之类的多余信息,所以使用socket编程发送请求:

3、client.py

import json
import socket

# 发送的数据格式必须满足这样的
"""
method: 服务名称、方法名称
result:返回的结果
id:随意指定一个值,如果不指定,返回值id为None
"""
request = {
    "method": "HelloWorldService.HelloWorld",
    "params": ["bily"],
    "id": 0
}

client = socket.create_connection(("127.0.0.1", 8000))
client.sendall(json.dumps(request).encode())

# 设置一次性接收的数据大小
response = client.recv(4096)
response = json.loads(response.decode())
print(response)

# 关闭连接
client.close()

请求结果:

{'id': 0, 'result': 'hellobily', 'error': None}

三、基于http实现RPC

 1、server.go

package main

import (
    "io"
    "net/http"
    "net/rpc"
    "net/rpc/jsonrpc"
)

type HelloWorldService struct {
}

func (s *HelloWorldService) HelloWorld(request string, response *string) error {
    *response = "hello" + request
    return nil
}

func main() {
    _ = rpc.RegisterName("HelloWorldService", new(HelloWorldService))
    http.HandleFunc("/jsonrpc", func(writer http.ResponseWriter, request *http.Request) {
        var conn io.ReadWriteCloser = struct {
            io.Writer
            io.ReadCloser
        }{
            ReadCloser: request.Body,
            Writer:     writer,
        }
        _ = rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
    })
    _ = http.ListenAndServe(":8000", nil)

}

2、client.go

import requests

request = {
    "method": "HelloWorldService.HelloWorld",
    "params": ["karry"],
    "id": 0
}

res = requests.post("http://127.0.0.1:8000/jsonrpc", json=request)
print(res.text)

四、封装代理

在之前的调用中,存在一些明显的问题:

  • 服务端中,业务代码与RPC通信底层混在一起,需要一种结构层次,将其分离
  • 客户端中,每次调用都需要知道业务端的服务名称与方法名称,将其单独封装

这样就引出服务端代理Server Stub和客户端代理Client Stub:

1、目录结构

├─client
│      client.go
│
├─client_stub
│      client_stub.go
│
├─handler
│      handler.go
│
├─server
│      server.go
│
└─server_stub
        server_stub.go

2、client.go

package main

import (
    "fmt"
    "go_rpc_project/stub_rpc/client_stub"
)

func main() {
    // 建立连接
    client := client_stub.NewHelloWorldServiceClient("tcp", "127.0.0.1:8000")
    // 调用业务函数HelloWorld
    var response string
    _ = client.HelloWorld("harry", &response)
    fmt.Println(response)

}

3、client_stub.go

package client_stub

import (
    "go_rpc_project/stub_rpc/handler"
    "log"
    "net/rpc"
)

type HelloWorldServiceStub struct {
    *rpc.Client
}

func NewHelloWorldServiceClient(protcol string, address string) HelloWorldServiceStub {
    conn, err := rpc.Dial(protcol, address)
    if err != nil {
        log.Fatal("拨号错误", err)
    }
    return HelloWorldServiceStub{conn}
}

func (c *HelloWorldServiceStub) HelloWorld(request string, response *string) error {
    err := c.Call(handler.HelloWorldServiceName+".HelloWorld", request, response)
    if err != nil {
        log.Fatal("调用服务失败", err)
    }
    return nil
}

4、handler.go

package handler

const HelloWorldServiceName = "handler/HelloWorldService"

type HelloWorldService struct {
}

func (s *HelloWorldService) HelloWorld(request string, response *string) error {
    *response = "hello" + request
    return nil
}

5、server.go

package main

import (
    "go_rpc_project/stub_rpc/handler"
    "go_rpc_project/stub_rpc/server_stub"
    "net"
    "net/rpc"
)

func main() {
    // 实例化一个server
    listener, _ := net.Listen("tcp", ":8000")
    // 注册,将业务逻辑代码注册到代理中
    _ = server_stub.RegisterServicer(&handler.HelloWorldService{})
    // 启动服务
    for {
        conn, _ := listener.Accept()
        go rpc.ServeConn(conn)
    }
}

6、server_stub.go

package server_stub

import (
    "go_rpc_project/stub_rpc/handler"
    "net/rpc"
)

type HelloWorldServicer interface {
    HelloWorld(request string, response *string) error
}

func RegisterServicer(srv HelloWorldServicer) error {
    return rpc.RegisterName(handler.HelloWorldServiceName, srv)
}

 

作者:iveBoy
出处:http://www.cnblogs.com/shenjianping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。

推荐这些文章:

go语言rpc开发

1、服务端
Go语言的RPC包的路径为net/rpc,也就是放在了net包。

rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在“HelloService”服务空间之下。然后我们建立一个唯一的TCP链接,并且通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。
2、客户端

rpc.Dial拨号RPC服务,然后通过client.Call调用具体的RPC方法。
  在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别我们定义RPC方法的两个参数。
 
...

Python语言下的RPC开发

一、httpserver实现rpc
 1、server.py

from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qsl
import json

host = ('', 8000)

class UserHandler(BaseHTTPRequestHandler):

def do_GET(self):
"""处理get请求"""
parse_url = urlparse(self.pat...

go语言下的grpc开发

1、下载protoc
地址:https://github.com/google/protobuf/releases
window:    下载: protoc-3.3.0-win32.zip    解压,把bin目录下的protoc.exe复制到GOPATH/bin下,GOPATH/bin加入环境变量。    当然也可放在其他目录,需加入环境变量,能让系统找到protoc.exelinux:    下载:protoc-3.3.0-linux-x86_64.zip 或 protoc-3.3.0-linux-x86_...

go 及时通信-建立客户端

package main

import (
"fmt"
"net"
)

type Client struct {
ServerIp string
ServerPort int
Name string
Conn net.Conn
}

func NewClient(serverIp string, serverPort int) *Client {
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
}

conn, err := net.Dial(...

go语言之UDP通信

服务端

package main

import (
"fmt"
"net"
)

func main() {
listen, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
fmt.Println("listen failed, err:", err)
return
}
defer listen.Close()
for {
var data [1024]byte
n, addr, err...

go-micro开发RPC服务的方法及其运行原理

go-micro是一个知名的golang微服务框架,最新版本是v4,这篇文章将介绍go-micro v4开发RPC服务的方法及其运作原理。
基本概念
go-micro有几个重要的概念,后边开发RPC服务和介绍其运行原理的时候会用到,这里先熟悉下:

Service:代表一个go-micro应用程序,Service中包括:Server、Client、Broker、Transport、Registry、Config、Store、Cache等程序运行所需的各个模块。
Server:代表一个go-micro服务器,主要函数包括:Start、Stop、Handle、Subscribe。默认创建的Se...

go 及时通信-命令行解析

package main

import (
"flag"
"fmt"
"net"
)

type Client struct {
ServerIp string
ServerPort int
Name string
Conn net.Conn
}

func NewClient(serverIp string, serverPort int) *Client {
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
}

conn, err := n...

谈谈如何使用Netty开发实现高性能的RPC服务器

  RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议。说的再直白一点,就是客户端在不必知道调用细节的前提之下,调用远程计算机上运行的某个对象,使用起来就像调用本地的对象一样。目前典型的RPC实现框架有:Thrift(facebook开源)、Dubbo(alibaba开源)等等。RPC框架针对网络协议、网络I/O模型的封装是透明的,对于调用的客户端而言,它就认为自己在调用本地的一个对象。至于传输层上,运用的是TCP协议、UDP协议、亦或是HTTP协议,一概不关心。从网络I/O模型上...

go的http-simple-server

点击查看代码
package main

import (
"fmt"
"net/http"
)

func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("handle hello")
fmt.Fprintf(w, "hello")
}

func main() {
http.HandleFunc("/", Hello)
err := http.ListenAndServe("0.0.0.0:8888", nil)
if err != nil {
fmt.Println("Http Listen ...

go interface nil 比较

package main

import "fmt"

type MyselfError struct{}

func (m *MyselfError) Error() string {
return "实现 error 接口的 Error 方法"
}

func someWork() *MyselfError {
return nil
}

func f1() error {
return nil
}

func main() {

var err error
err = f1()
fmt.Println(err == nil)

err = someWork()
fmt....

文章标题:Go语言下RPC的开发
文章链接:https://www.dianjilingqu.com/50878.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。
THE END
< <上一篇
下一篇>>