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时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结. 其实我也是一个新手,机缘巧合 ...
随机推荐
- how to use bookdown
模板下载 {#moban} 谢益辉 通用模板:https://github.com/rstudio/bookdown-demo 李东风中文模板: http://www.math.pku.edu.cn/ ...
- H5-安卓和ios调用相机和相册
<input v-if="ipshow" type="file" accept="image/*" name="file1& ...
- 每天进步一点点------Modelsim添加Xilinx仿真库的详细步骤
Modelsim,可以选型SE和XE两个版本.Modelsim XE可以直接被ISE调用,而Modelsim SE需要手动添加仿真库.但SE版和OEM版在功能和性能方面有较大差别,比如对于大家都关心的 ...
- nat123+nginx实现外网访问本机IIS发布的系统
故事开端(前因) 嗯,内网其实是校园网络,服务器呢,不是阿里云.腾讯云之类的云服务器,而是自己正在码字的笔记本电脑:有公网IP吗?没有!校园IP分配的IP固定不?不固定,动态分配的,额~~~. 我想想 ...
- BZOJ 3698: XWW的难题
Description XWW是个影响力很大的人,他有很多的追随者.这些追随者都想要加入XWW教成为XWW的教徒.但是这并不容易,需要通过XWW的考核. XWW给你出了这么一个难题:XWW给你一个N ...
- LeetCode Subarray Product Less Than K 题解 双指针+单调性
题意 给定一个正整数数组和K,数有多少个连续子数组满足: 数组中所有的元素的积小于K. 思路 依旧是双指针的思路 我们首先固定右指针r. 现在子数组的最右边的元素是nums[r]. 我们让这个子数组尽 ...
- 5G三大应用场景
5G三大应用场景:eMBB(增强移动宽带).eMTC(海量物联).uRLLC(高可靠低时延连接) ------20191215闪
- 专项:Vuejs面试题集合
参考网络资源:https://segmentfault.com/a/1190000012315822 1.active-class是哪个组件的属性? 答:active-class是vue-router ...
- CodeForces 1144B
原题https://vjudge.net/problem/CodeForces-1144B #include<bits/stdc++.h> using namespace std; vec ...
- PP: Tripoles: A new class of relationships in time series data
Problem: ?? mining relationships in time series data; A new class of relationships in time series da ...