golang中的net/rpc包
本文先介绍RPC,然后go原生对RPC的使用,之后是介绍go语言中有哪些RPC框架以及一些其他常见的框架,最后是探究go语言中rpc的源码。
(1)首先介绍下什么RPC?
(2)RPC可以做什么?
(3)RPC与REST风格的API有什么不同?
(4)go语言中使用RPC
(5)常见的RPC框架
(6)RPC源码探究
一、什么是RPC?
RPC是Remote Procedure Call,其中文意思就是 远程过程调用,可以理解成 一台主机上的进程调用另一台主机的进程服务,由一方为其它若干个主机提供服务。从表面上看非常类似于http API,RPC的目的可以屏蔽不同语言之间的关联,最大程度上进行解耦,调用方不需要知道服务方是用什么语言编写和其实现,只要知道服务方的RPC对外服务就行。其本质就是进程间的一种通信方式,可以是本机也可以是不同主机。
二、RPC可以做什么?
API、进程间通信,主要用于分布式应用间通信。
三、RPC与REST风格API有什么不同?
本质区别就是REST是使用http协议,相比RPC的实现协议传输会传更多的内容,但是两个可以做相同的事情。
四、go语言中使用RPC
RPC分服务提供和服务使用,也就是服务端和客户端,我们先来编写服务端:
服务端:
1)服务内容
// 对外提供的必须是对外可见的类型
type Arith int // 对外提供的方法也要是对外可见的类型,其中要被注册的服务至少要有一个对外可见的方法,不然执行的时候的时候会打印错误,还有对外服务的必须是方法第一个参数必须是对外可见的类型,第二个参数可以是对外可见类型或者是内置类型,然后必须要有一个返回值。
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
2)端口监听
l, err := net.Listen("tcp", ":12345")
3)注册服务
t := new(GetServerTime)
// 注册到RPC
err = rpc.Register(t)
4)开启服务
rpc.HandleHTTP()
5)启动HTTP服务
http.Serve(l, nil)
相比服务端,客户端的编写会简单很多,
客户端:
1)连接服务RPC
client, err := rpc.DialHTTP(协议, ip:端口)
2)调用RPC服务
有两种方式:同步或异步
// 同步方式
client.Call("rpc上的公开类名:公开方法", 第一个传入的变量, 第二个传入的变量)
// 异步方式
divCall := client.Go("rpc上的公开类名:公开方法", 第一次传入的变量, 第二个传入的变量, nil)
replyCall := <- divCall.Done 阻塞,等待异步完成
最基本的流程就是这样,然后下面一段例子,是从标准库那边copy过来的:
public.go
package public
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
server.go
package main import (
"Songzhibin/test/rpc/public"
"errors"
"log"
"net"
"net/http"
"net/rpc"
) type Arith int func (t *Arith) Multiply(args *public.Args, reply *int) error {
*reply = args.A * args.B
return nil
} func (t *Arith) Divide(args *public.Args, quo *public.Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
} func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
select {}
}
client.go
// 客户端
package main import (
"Songzhibin/test/rpc/public"
"fmt"
"log"
"net/rpc"
) func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing:", err)
}
// 然后,客户端可以执行远程调用: // Synchronous call
args := &public.Args{7, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
// 或: // Asynchronous call
quotient := new(public.Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.
fmt.Printf("%#v\n", replyCall)
}
TCP-RPC(GOB)
public.go
package public
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
server.go
package main import (
"Songzhibin/test/rpc/public"
"errors"
"fmt"
"net"
"net/rpc"
) type Arith int func (t *Arith) Multiply(args *public.Args, reply *int) error {
*reply = args.A * args.B
return nil
} func (t *Arith) Divide(args *public.Args, quo *public.Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
} func main() {
arith := new(Arith)
rpc.Register(arith)
// rpc.HandleHTTP() 不使用 HandleHTTP()
l, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println(err)
return
}
for {
// 获取连接
conn, err := l.Accept()
if err != nil {
fmt.Println(err)
return
}
rpc.ServeConn(conn)
}
select {}
}
client.go
package main import (
"Songzhibin/test/rpc/public"
"fmt"
"log"
"net/rpc"
) func main() {
// 这里只需要将 DialHTTP改为Dial 即可
client, err := rpc.Dial("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing:", err)
}
// 然后,客户端可以执行远程调用: // Synchronous call
args := &public.Args{7, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
// 或: // Asynchronous call
quotient := new(public.Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.
fmt.Printf("%#v\n", replyCall)
}
Json-RPC(TCP)
public.go
package public
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
package main import (
"Songzhibin/test/rpc/public"
"errors"
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
) type Arith int func (t *Arith) Multiply(args *public.Args, reply *int) error {
*reply = args.A * args.B
return nil
} func (t *Arith) Divide(args *public.Args, quo *public.Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
} func main() {
arith := new(Arith)
rpc.Register(arith)
// rpc.HandleHTTP() 不使用 HandleHTTP()
l, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println(err)
return
}
for {
// 获取连接
conn, err := l.Accept()
if err != nil {
fmt.Println(err)
return
}
// 将 rpc.ServeConn 改为jsonrpc.ServeConn
jsonrpc.ServeConn(conn)
}
select {}
}
client.go
package main import (
"Songzhibin/test/rpc/public"
"fmt"
"log"
"net/rpc/jsonrpc"
) func main() {
// 这里只需要将 rpc.Dial改为jsonrpc.Dial 即可
client, err := jsonrpc.Dial("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing:", err)
}
// 然后,客户端可以执行远程调用: // Synchronous call
args := &public.Args{7, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
// 或: // Asynchronous call
quotient := new(public.Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.
fmt.Printf("%#v\n", replyCall)
}
golang中的net/rpc包的更多相关文章
- golang中tcp socket粘包问题和处理
转自:http://www.01happy.com/golang-tcp-socket-adhere/ 在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据 ...
- golang中的原子操作atomic包
1. 概念 原子操作 atomic 包 加锁操作涉及到内核态的上下文切换,比较耗时,代价高, 针对基本数据类型我们还可以使用原子操作来保证并发的安全, 因为原子操作是go语言提供的方法,我们在用户态就 ...
- golang中的rpc包用法
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我所在公司的项目是采用基于Restful的微服务架构,随着微服 ...
- golang中的RPC开发-2
RPC简介 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程 如果 ...
- google的grpc在golang中的使用
GRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x. 前面写过一篇golang标准库的rpc包的用法,这篇文章接着讲一 ...
- golang中并发sync和channel
golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...
- golang中的reflect包用法
最近在写一个自动生成api文档的功能,用到了reflect包来给结构体赋值,给空数组新增一个元素,这样只要定义一个input结构体和一个output的结构体,并填写一些相关tag信息,就能使用程序来生 ...
- golang 中 sync包的 WaitGroup
golang 中的 sync 包有一个很有用的功能,就是 WaitGroup 先说说 WaitGroup 的用途:它能够一直等到所有的 goroutine 执行完成,并且阻塞主线程的执行,直到所有的 ...
- 在Golang中如何正确地使用database/sql包访问数据库
本文记录了我在实际工作中关于数据库操作上一些小经验,也是新手入门golang时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结. 其实我也是一个新手,机缘巧合 ...
随机推荐
- 阻止a链接跳转的点击事件
<a href="http://www.baidu.com" id="btn">按钮</a> <script> docume ...
- Mybatis常见面试题汇总
Mybatis常见面试题汇总 最近在复习整理Mybatis的相关知识,针对面试中的典型问题,结合相关书籍和网上相关帖子,做如下整理. ================================= ...
- PAT (Basic Level) Practice (中文)1030 完美数列 (25 分) (有点意思)
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列. 现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列. 输入格 ...
- 题解 AT5632 【Sum of Two Integers】
在幼儿园的时候,我们就学习过把一个数分成\(a\)与\(b\),我们只需要用计算机来模拟这个过程就可以了. 我们先从奇数开始看起,以\(5\)为例: 我们可以发现,\(5\)可以分成\(1\)和\(4 ...
- 爬格子呀--IEEE极限编程大赛留念
10.14,坐标:电子科技大学 24h,不间断的编程,感觉还是很爽的. 排名一般,但是这是开始,未来还很远. 题目举例1: 广袤的非洲大草原上,狮子居住在一个个的网格里,他们的势力范围会以曼哈顿路程的 ...
- jQuery 判断页面上下滚动
var t = 0, b = 0; $(window).scroll(function(){ t = $(this).scrollTop(); if(b < t){ console.log('向 ...
- LeetCode longest substring without repeating characters 题解 Hash表
题目 Given a string, find the length of the longest substring without repeating characters. Example 1: ...
- 批量获取mysql数据库实例指定参数的值
需求:需要对比所有mysql数据库实例上面的指定参数配置情况,同时需要需要能看到如ip,端口,master or slave,毕竟主和从参数不一样还是有可能的. 说明:必须要有个数据库存储所有是数据库 ...
- 19新生赛 谁更nb
题目描述: 有一堆石子共有N个.syx xxh两个人轮流拿,syx先拿.每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获 胜.syx xxh都非常聪明,拿石子的过程中不会出现失误.给出N和K,问最后 ...
- TCL namespace
命名空间可从Tcl 8.0版开始使用.引入命名空间之前,有一个全局范围.现在有了命名空间,我们可以分区全局范围. 创建命名空间: 结果:33 嵌套命名空间: 结果: test1 test2 导入命名空 ...