Golang RPC Go标准库的简单实现 介绍+使用
如果我们想在服务端为客户端提供一个打印一个输入字段的服务,借助net/rpc,我们可以这样实现:
初步实现
服务端
package main
import (
"log"
"net"
"net/rpc"
)
//构造一个类 名字是HelloService
type HelloService struct{}
//给这个类绑定一个Hello方法 实现打印的功能,这个方法只能有两个可序列化的参数,其中第二个参数是指针类型,并且返回一个error类型,同时必须是公开的方法
func (item *HelloService) Hello(req string, reply *string) error {
*reply = "server-hello " + req
return nil
}
func main() {
//将HelloService的对象注册为一个RPC服务
rpc.RegisterName("HelloService", new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error:", err)
}
//建立一个唯一的TCP链接,并且通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error:", err)
}
rpc.ServeConn(conn)
}
客户端
package main
import (
"fmt"
"log"
"net/rpc"
)
func main() {
//通过rpc.Dial拨号RPC服务
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("dialing", err)
}
var reply string
//通过client.Call调用具体的RPC方法
//在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,
//第二和第三个参数分别我们定义RPC方法的两个参数。
err = client.Call("HelloService.Hello", " client-Hello", &reply)
if err != nil {
log.Fatal(err)
}
fmt.Println(reply)
}
运行结果:
server-hello client-Hello
重构与优化
在涉及RPC的应用中,作为开发人员一般至少有三种角色:首先是服务端实现RPC方法的开发人员,其次是客户端调用RPC方法的人员,最后也是最重要的是制定服务端和客户端RPC接口规范的设计人员。在前面的例子中我们为了简化将以上几种角色的工作全部放到了一起,虽然看似实现简单,但是不利于后期的维护和工作的切割。我们将RPC服务的接口规范分为三个部分:首先是服务的名字,然后是服务要实现的详细方法列表,最后是注册该类型服务的函数。为了避免名字冲突,我们在RPC服务的名字中增加了包路径前缀(这个是RPC服务抽象的包路径,并非完全等价Go语言的包路径)。RegisterHelloService注册服务时,编译器会要求传入的对象满足HelloServiceInterface接口。
服务端
package main
import (
"log"
"net"
"net/rpc"
)
type HelloService struct{}
func (item *HelloService) Hello(req string, reply *string) error {
*reply = "server-hello " + req
return nil
}
//明确服务的名字
const HelloServiceName = "path/to/pkg.HelloService"
//明确服务的接口 说明服务要实现的详细方法列表
type HelloServiceInterface = interface {
Hello(request string, reply *string) error
}
//注册该类型服务的函数 注册服务时,编译器会要求传入的对象满足HelloServiceInterface接口
func RegisterHelloService(svc HelloServiceInterface) error {
return rpc.RegisterName(HelloServiceName, svc)
}
func main() {
RegisterHelloService(new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error", err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error:", err)
}
go rpc.ServeConn(conn)
}
}
客户端
package main
import (
"fmt"
"log"
"net/rpc"
)
//明确服务的名字
const HelloServiceName = "path/to/pkg.HelloService"
func main() {
//通过rpc.Dial拨号RPC服务
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("dialing", err)
}
var reply string
//通过client.Call调用具体的RPC方法
//在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,
//第二和第三个参数分别我们定义RPC方法的两个参数。
err = client.Call(HelloServiceName+".Hello", " client-Hello", &reply)
if err != nil {
log.Fatal(err)
}
fmt.Println(reply)
}
运行结果
server-hello client-Hello
参考: https://chai2010.cn/advanced-...
推荐阅读
CSS in JS 之 Styled-components 用法
npm i styled-components基本用法 {代码...} 支持sass/less语法 {代码...} 可透传props 以及 基于props做样式判断 {代码...} 样式化任意组件 {代码...} css的复用、扩展、增强 {代码...} 添加动画 定义keyframes ...
LiberHome赞 1阅读 83
Golang 中 []byte 与 string 转换
string 类型和 []byte 类型是我们编程时最常使用到的数据结构。本文将探讨两者之间的转换方式,通过分析它们之间的内在联系来拨开迷雾。
机器铃砍菜刀赞 24阅读 58.3k评论 2
年度最佳【golang】map详解
这篇文章主要讲 map 的赋值、删除、查询、扩容的具体执行过程,仍然是从底层的角度展开。结合源码,看完本文一定会彻底明白 map 底层原理。
去去1002赞 16阅读 11.6k评论 2
年度最佳【golang】GMP调度详解
Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底的. 这篇文章将通过分析...
去去1002赞 15阅读 12k评论 4
万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...
JavaGuide赞 8阅读 1.7k
数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...
白鲸鱼赞 9阅读 5.3k
【golang】sync.WaitGroup详解
上一期中,我们介绍了 sync.Once 如何保障 exactly once 语义,本期文章我们介绍 package sync 下的另一个工具类:sync.WaitGroup。
去去1002赞 13阅读 31.3k评论 2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。