golang中的rpc开发
golang中实现RPC非常简单,官方提供了封装好的库,还有一些第三方的库
golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp和http数据传输方式,由于其他语言不支持gob编解码方式,所以golang的RPC只支持golang开发的服务器与客户端之间的交互
官方还提供了net/rpc/jsonrpc库实现RPC方法,jsonrpc采用JSON进行数据编解码,因而支持跨语言调用,目前jsonrpc库是基于tcp协议实现的,同时也支持http传输方式
go快速体验rpc开发
- 服务端
package main
import (
"net"
"net/rpc"
)
type HelloService struct {}
func (s *HelloService) Hello(request string, reply *string) error {
// 返回值是通过修改reply的值
*reply = "hello " + request
return nil
}
func main() {
// 1. 实例化一个server
listener, _ := net.Listen("tcp", ":1234")
// 2. 注册处理逻辑handler
_ = rpc.RegisterName("HelloService", &HelloService{})
// 3. 启动server
conn, _ := listener.Accept() // 当一个新的连接进来的时候
rpc.ServeConn(conn)
}
- 客户端
package main
import (
"fmt"
"net/rpc"
)
func main() {
// 1. 建立连接
client, err := rpc.Dial("tcp", "192.168.0.101:1234")
if err != nil {
panic("连接失败")
}
//var ret *string nil 内存地址没有 只声明不初始化没有办法赋值,所以需要使用new方法声明并初始化
// 方法一
//var reply = new(string)
// 方法二:
var reply string // 空字符串,已经有内存地址了
if err = client.Call("HelloService.Hello", "马亚南", &reply); err != nil {
panic(err)
}
fmt.Println(reply)
}
- net/rpc完成的 1. call id的映射 2. 序列化和反序列化(编码和解码)
- net完成的 网络通信:tcp
替换rpc的序列化协议为json
- go语言的rpc序列化协议是什么:Gob编码
- 能够替换成常见的序列化
go服务端
package main
import (
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type HelloRPC struct {}
func (h *HelloRPC) Hello(request string, reply *string) error {
*reply = "hello " + request
return nil
}
func main() {
listener, _ := net.Listen("tcp", ":1285")
rpc.RegisterName("HelloRPC", &HelloRPC{})
for {
conn, _ := listener.Accept()
// 不使用go默认的Gob序列化方式,而是改成json编码方式
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
go客户端
package main
import (
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func main() {
// rpc.Dial拨号会走Gob序列化协议,所以需要改成net.Dial
// rpc.Dial返回的是client,而net.Dial返回的是conn
conn, _ := net.Dial("tcp", "127.0.0.1:1285")
var reply = new(string)
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
_ = client.Call("HelloRPC.Hello", "马馨彤", reply)
fmt.Println(*reply)
}
python客户端请求上面的go服务端,完成多语言rpc通信
import socket
import json
send_data = {
"id": 0,
"params": ["abc", "马馨彤22"],
"method": "HelloRPC.Hello",
}
# tcp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# tcp_client_socket.connect(("127.0.0.1", 1285))
# 上面两句可以用下面一句代替
tcp_client_socket = socket.create_connection(("localhost", 1285))
tcp_client_socket.send(json.dumps(send_data).encode())
# 获取服务器返回的数据
rsp = tcp_client_socket.recv(1024)
rsp = json.loads(rsp.decode())
print(rsp) # {'id': 0, 'result': 'hello abc', 'error': None}
tcp_client_socket.close()
针对上面的升级版本
go服务端接收的request是一个json,python客户端传递的params列表中是一个json数据
- go服务端
点击查看代码
package main
import (
"encoding/json"
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"strconv"
)
type HelloRPC struct {}
func (h *HelloRPC) Hello(request string, reply *string) error {
fmt.Println(request)
var m1 map[string]interface{}
json.Unmarshal([]byte(request), &m1)
i, _ := strconv.Atoi(fmt.Sprintf("%1.0f", m1["age"]))
m1["age"] = float64(i + 1)
rsp, _ := json.Marshal(&m1)
*reply = "hello " + string(rsp)
return nil
}
func main() {
listener, _ := net.Listen("tcp", ":1285")
rpc.RegisterName("HelloRPC", &HelloRPC{})
for {
conn, _ := listener.Accept()
// 不使用go默认的Gob序列化方式,而是改成json编码方式
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
2. python客户端
点击查看代码
import socket
import json
send_data = {
"id": 0,
"params": ['{"name": "Lisi","age": 18}'],
"method": "HelloRPC.Hello",
}
# tcp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# tcp_client_socket.connect(("127.0.0.1", 1285))
# 上面两句可以用下面一句代替
tcp_client_socket = socket.create_connection(("localhost", 1285))
tcp_client_socket.send(json.dumps(send_data).encode())
# 获取服务器返回的数据
rsp = tcp_client_socket.recv(1024)
rsp = json.loads(rsp.decode())
print(rsp) # {'id': 0, 'result': 'hello abc', 'error': None}
tcp_client_socket.close()
替换rpc的传输协议为http
同时使用json编码解码
- go服务端
package main
import (
"io"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
)
type HelloRPC struct {}
func (h *HelloRPC) Hello(request string, reply *string) error {
*reply = "hello " + request
return nil
}
func main() {
_ = rpc.RegisterName("HelloRPC", &HelloRPC{})
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(":1234", nil)
}
- python客户端
import requests
request = {
"id": 0,
"params": ["马艳娜"],
"method": "HelloRPC.Hello"
}
rsp = requests.post("http://127.0.0.1:1234/jsonrpc", json=request)
print(rsp.text)
进一步改造rpc调用的代码(封装达到grpc的使用效果)
目录结构图
server.go
package main
import (
"goRPC/new_helloworld/handler"
"goRPC/new_helloworld/server_proxy"
"net"
"net/rpc"
)
func main() {
// 1. 实例化一个server
listener, _ := net.Listen("tcp", ":1234")
// 2. 注册处理逻辑handler
_ = server_proxy.RegisterHelloService(&handler.HelloService{})
// 3. 启动server
for {
conn, _ := listener.Accept() // 当一个新的连接进来的时候
go rpc.ServeConn(conn)
}
}
- handler.go
package handler
const HelloServiceName = "handler/HelloService"
// 我们关心的是HelloService的名字呢,还是结构体中的Hello方法呢?
type HelloService struct {}
func (s *HelloService) Hello(request string, reply *string) error {
// 返回值是通过修改reply的值
*reply = "hello " + request
return nil
}
- server_proxy.go
package server_proxy
import (
"goRPC/new_helloworld/handler"
"net/rpc"
)
type HelloServer interface {
Hello(request string, reply *string) error
}
// 如何做到与handler中的结构体名称解耦? 我们关心的是函数: 鸭子类型
func RegisterHelloService(srv HelloServer) error {
return rpc.RegisterName(handler.HelloServiceName, srv)
}
- client.go
package main
import (
"fmt"
"goRPC/new_helloworld/client_proxy"
)
func main() {
// 建立链接
client := client_proxy.NewHelloServiceClient("tcp", "127.0.0.1:1234")
// 指向写业务逻辑,不想关注每个函数的名称
var reply = new(string)
err := client.Hello("马亚南121", reply)
if err != nil {
panic(err)
}
fmt.Println(*reply)
}
- client_proxy.go
package client_proxy
import (
"goRPC/new_helloworld/handler"
"net/rpc"
)
type HelloServiceStub struct {
*rpc.Client
}
// 在go语言中没有类、对象,就意味着没有初始化方法
func NewHelloServiceClient(protocol, address string) *HelloServiceStub {
client, _ := rpc.Dial(protocol, address)
return &HelloServiceStub{Client: client}
}
func (c *HelloServiceStub) Hello(request string, reply *string) error {
return c.Call(handler.HelloServiceName + ".Hello", request, reply)
}
- 上面这些概念在grpc中都有相关对应
- 发自灵魂的拷问:server_proxy和client_proxy能否自动生成啊?为多种语言生成?
- 答:上面两个问题都能解决: protobuf + grpc
golang中的rpc开发的更多相关文章
- golang中的RPC开发-2
RPC简介 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程 如果 ...
- golang中的rpc包用法
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我所在公司的项目是采用基于Restful的微服务架构,随着微服 ...
- golang - gob与rpc
今天和大家聊聊golang中怎么使用rpc,rpc数据传输会涉及到gob编码,所以先讲讲gob,别担心,就算你完全没有接触过gob与rpc,只要知道rpc的中文是远程过程调用,剩下的我都能给你讲明白( ...
- springcloud中使用dubbo开发rpc服务及调用
spring cloud中基于springboot开发的微服务,是基于http的rest接口,也可以开发基于dubbo的rpc接口. 一,创建goodsService模块 1, 在创建的goodsSe ...
- golang中的net/rpc包
本文先介绍RPC,然后go原生对RPC的使用,之后是介绍go语言中有哪些RPC框架以及一些其他常见的框架,最后是探究go语言中rpc的源码. (1)首先介绍下什么RPC? (2)RPC可以做什么? ( ...
- google的grpc在golang中的使用
GRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x. 前面写过一篇golang标准库的rpc包的用法,这篇文章接着讲一 ...
- Visual Studio Code中配置GO开发环境
在Visual Studio Code中配置GO开发环境 一.GO语言安装 详情查看:GO语言下载.安装.配置 二.GoLang插件介绍 对于Visual Studio Code开发工具,有一款优秀的 ...
- Golang中使用lua进行扩展
前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与go ...
- golang中Context的使用场景
golang中Context的使用场景 context在Go1.7之后就进入标准库中了.它主要的用处如果用一句话来说,是在于控制goroutine的生命周期.当一个计算任务被goroutine承接了之 ...
随机推荐
- 输入npm install 报错npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! node-sass@4.13.1 postinstall: `node scripts/build.js`
输入npm install 报以下错误 npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! node-sass@4.13.1 postinstall: ...
- vue的一些细节
注意区别 //鼠标滚轮事件 @wheel = "demo"demo()注意执行顺序,用户滚动鼠标滚轮,触发事件执行demo()函数,函数执行完毕后,页面滚动条滚动所以,当demo( ...
- 第一篇CSDN博客,大家好!
大家好,我是负雪明烛! 我这昵称的来源是喜欢一句很有意蕴的古诗--苍山负雪,明烛天南. 我喜欢这句诗,很多的账号都用了这个"负雪明烛"的昵称,如果大家在其他地方看到叫这个名字的人, ...
- 【LeetCode】953. Verifying an Alien Dictionary 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 【LeetCode】525. Contiguous Array 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 累积和 日期 题目地址:https://leetco ...
- 【LeetCode】458. Poor Pigs 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- Chapter 3 Observational Studies
目录 概 3.1 3.2 Exchangeability 3.3 Positivity 3.4 Consistency First Second Fine Point 3.1 Identifiabil ...
- 取代 Maven?这款项目构建工具性能提升 300%
在 GitHub 上闲逛的时候,发现了一个新的项目:maven-mvnd,持续霸占 GitHub trending 榜单好几天了. maven-mvnd,可以读作 Maven Daemon,译作 Ma ...
- PowerDotNet平台化软件架构设计与实现系列(11):日志平台
所有后端应用几乎都会记录日志,日志系统可以统一抽象出来提供服务. 最近被Log4j2的安全漏洞刷屏了,作为开发人员的我只能咩哈哈几次表示日志处理太难了,只有折腾过的人才知道这里面的艰辛啊. 在实现Po ...
- 如何优雅地读写HttpServletRequest和HttpServletResponse的请求体
最近很多交互要同原生的HttpServletRequest和HttpServletResponse打交道.从HttpServletRequest中读取body数据封装成某种数据结构:向HttpServ ...