GoLang下的rpc开发

GoLang下的rpc开发

Python下的rpc开发可阅读我的另一篇文章:Python下的rpc开发

1、Go语言的rpc之hello world

Go语言的rpc包的是建立在net包基础之上的(内置)。基于TCP协议

1.1 服务端

server.go

package main

import (
	"net"
	"net/rpc"
)

type HelloService struct{}

func (h *HelloService) Hello(request string, reply *string) error {
	*reply = "hello " + request

	return nil
}

func main() {
	// 1. 实例化server
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		panic("端口监听失败")
	}

	// 2. 注册处理逻辑handle
	_ = rpc.RegisterName("HelloService", &HelloService{})

	// 3.启动服务
	conn, err := listener.Accept()
	if err != nil {
		panic("建立连接失败")
	}
	rpc.ServeConn(conn)
}

说明:
1、Hello方法必须满足Go语言的RPC规则:方法只能有两个可序列化的参数,其中第二个参数是指针类型,并且返回一个error类型,同时必须是公开的方法,然后就可以将HelloService类型的对象注册为一个RPC服务:(TCP RPC服务)
2、其中rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在“HelloService”服务空间之下。
3、建立一个唯一的TCP链接,并且通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务

1.2 客户端

client.go

package main

import (
	"fmt"
	"net/rpc"
)

func main() {
	// 建立连接
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		panic("dialing fail")
	}

	var reply string
	err = client.Call("HelloService.Hello", "jason", &reply)
	if err != nil {
		panic("response fail")
	}

	fmt.Println(reply)
}

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

2、基于json实现rpc的调用

  • 仍然基于内置的net库实现,基于TCP协议
  • 标准库的RPC默认采用Go语言特有的gob编码,因此从其它语言调用Go语言实现的RPC服务将比较困难。在互联网的微服务时代,每个RPC以及服务的使用者都可能采用不同的编程语言,因此跨语言是互联网时代RPC的一个首要条件。得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言支持的。
  • Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另一个是RPC建立在抽象的io.ReadWriteCloser接口之上的,我们可以将RPC架设在不同的通讯协议之上。这里我们将尝试通过官方自带的net/rpc/jsonrpc扩展实现一个跨语言的PPC。

2.1 服务端

server.go

package main

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

type HelloService struct{}

func (h *HelloService) Hello(request string, reply *string) error {
	*reply = "hello " + request

	return nil
}

func main() {
	// 1. 实例化server
	listener, err := net.Listen("tcp", ":8888")
	if err != nil {
		panic("端口监听失败")
	}

	// 2. 注册处理逻辑handle
	_ = rpc.RegisterName("HelloService", &HelloService{})

	// 3.启动服务
	for {
		conn, err := listener.Accept()
		if err != nil {
			panic("建立连接失败")
		}
		// 传入参数是针对服务端的json编解码器
		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
	}
}


说明:代码中最大的变化是用rpc.ServeCodec函数替代了rpc.ServeConn函数,传入的参数是针对服务端的json编解码器

2.2 客户端

client.go

package main

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

func main() {
	// 建立连接
	conn, err := net.Dial("tcp", "localhost:8888")
	if err != nil {
		panic("dialing fail")
	}

	client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))

	var reply string
	err = client.Call("HelloService.Hello", "jason", &reply)
	if err != nil {
		panic("response fail")
	}

	fmt.Println(reply)
}

2.3 彩蛋:Python客户端尝试请求

go_client.py

import json
import socket

request = {
    "id": 0,
    "params": ["jason"],
    "method": "HelloService.Hello"
}
# socket连接
client = socket.create_connection(("localhost", 8888))
# 发送数据
client.sendall(json.dumps(request).encode())
# 接收数据
rsp = client.recv(1000)
rsp = json.loads(rsp.decode())

print(rsp)

在这里插入图片描述

3、基于http实现rpc的调用 (基础版)

3.1 服务端

server.go

package main

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

type HelloService struct{}

func (h *HelloService) Hello(request string, reply *string) error {
	*reply = "hello " + request

	return nil
}

func main() {
	_ = rpc.RegisterName("HelloService", &HelloService{})

	http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
		var conn io.ReadWriteCloser = struct {
			io.Writer
			io.ReadCloser
		}{
			ReadCloser: r.Body,
			Writer:     w,
		}
		_ = rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
	})
	_ = http.ListenAndServe(":1234", nil)
}

3.2 客户端

client.go

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	var p = struct {
		Id     int      `json:"id"`
		Params []string `json:"params"`
		Method string   `json:"method"`
	}{
		Id:     1,
		Params: []string{"jason"},
		Method: "HelloService.Hello",
	}

	b, _ := json.Marshal(p)
	text, _ := sendJsonPost("http://127.0.0.1:1234/jsonrpc", string(b))

	fmt.Println(text)
}

// 发送post请求
func sendJsonPost(targetUrl string, params string) (content string, err error) {
	var jsonStr = []byte(params)
	req, _ := http.NewRequest("POST", targetUrl, bytes.NewBuffer(jsonStr))

	req.Header.Set("X-Custom-Header", "myvalue")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}

	resp, err := client.Do(req)

	if err != nil {
		return "", errors.New(fmt.Sprintf("请求出错 %s", err))
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	body, _ := ioutil.ReadAll(resp.Body)

	return string(body), nil

}

在这里插入图片描述

4、基于http实现rpc的调用 (改进版)

4.1 公共文件

解耦:server端和client端统一serviceName

公共文件: handler.go

package handler

const HelloServiceName = "handler/HelloService"

type HelloService struct{}

func (h *HelloService) Hello(request string, reply *string) error {
	*reply = "hello " + request

	return nil
}

4.2 服务端代理

服务端代理: server_proxy.go

package server_proxy

import (
	"demo/http_forward/handler"
	"net/rpc"
)

// HelloServer 定义服务接口
type HelloServer interface {
	Hello(request string, reply *string) error
}

// RegisterHelloService 注册服务
func RegisterHelloService(srv HelloServer) error {
	return rpc.RegisterName(handler.HelloServiceName, srv)
}


4.3 服务端

服务端: server.go

package main

import (
	"demo/http_forward/handler"
	"demo/http_forward/server_proxy"
	"net"
	"net/rpc"
)

func main() {
	// helloService具体实现
	helloHandler := &handler.HelloService{}

	// 注册服务
	_ = server_proxy.RegisterHelloService(helloHandler)

	listener, err := net.Listen("tcp", ":8001")
	if err != nil {
		panic("监听端口失败")
	}

	conn, err := listener.Accept()
	if err != nil {
		panic("建立链接失败")
	}

	rpc.ServeConn(conn)
}

4.4 客户端代理

client_proxy.go

package client_proxy

import (
	"demo/http_forward/handler"
	"net/rpc"
)

// NewClient 客户端
func NewClient(protocol string, address string) HelloServiceStub {
	conn, err := rpc.Dial(protocol, address)
	if err != nil {
		panic("连接服务器错误")
	}
	return HelloServiceStub{conn}
}

type HelloServiceStub struct {
	*rpc.Client
}

func (c *HelloServiceStub) Hello(request string, reply *string) error {
	err := c.Call(handler.HelloServiceName+".Hello", request, reply)
	if err != nil {
		return err
	}
	return nil
}


4.5 客户端

client.go

package main

import (
	"demo/http_forward/client_proxy"
	"fmt"
)

func main() {
	client := client_proxy.NewClient("tcp", "127.0.0.1:8001")

	var reply string
	err := client.Hello("jason", &reply)
	if err != nil {
		panic("调用失败")
	}
	fmt.Println(reply)
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JasonHome

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值