本文先介绍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
}
server.go
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包的更多相关文章

  1. golang中tcp socket粘包问题和处理

    转自:http://www.01happy.com/golang-tcp-socket-adhere/ 在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据 ...

  2. golang中的原子操作atomic包

    1. 概念 原子操作 atomic 包 加锁操作涉及到内核态的上下文切换,比较耗时,代价高, 针对基本数据类型我们还可以使用原子操作来保证并发的安全, 因为原子操作是go语言提供的方法,我们在用户态就 ...

  3. golang中的rpc包用法

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我所在公司的项目是采用基于Restful的微服务架构,随着微服 ...

  4. golang中的RPC开发-2

    RPC简介 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程 如果 ...

  5. google的grpc在golang中的使用

    GRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x. 前面写过一篇golang标准库的rpc包的用法,这篇文章接着讲一 ...

  6. golang中并发sync和channel

    golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...

  7. golang中的reflect包用法

    最近在写一个自动生成api文档的功能,用到了reflect包来给结构体赋值,给空数组新增一个元素,这样只要定义一个input结构体和一个output的结构体,并填写一些相关tag信息,就能使用程序来生 ...

  8. golang 中 sync包的 WaitGroup

    golang 中的 sync 包有一个很有用的功能,就是 WaitGroup 先说说 WaitGroup 的用途:它能够一直等到所有的 goroutine 执行完成,并且阻塞主线程的执行,直到所有的 ...

  9. 在Golang中如何正确地使用database/sql包访问数据库

    本文记录了我在实际工作中关于数据库操作上一些小经验,也是新手入门golang时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结. 其实我也是一个新手,机缘巧合 ...

随机推荐

  1. gulp常用插件之gulp-size使用

    更多gulp常用插件使用请访问:gulp常用插件汇总 gulp-size这是一款显示项目的大小插件. 更多使用文档请点击访问gulp-size工具官网. 安装 一键安装不多解释 npm install ...

  2. CTF长久练习平台

    0x01 XCTF(攻防世界) 攻防世界是ctf爱好者很喜欢的一个平台,不仅是界面风格像大型游戏闯关,里面的各类题目涵盖的ctf题型很广,还分为新手区和进阶区两块: 并且可以在里面组队,做一道题还有相 ...

  3. CF571D Campus(19-1)

    题意 \(n\)个点,维护两个森林,这里\(A,B\)两个森林对应的点都是一样的,相当于对两个森林操作都会影响这\(n\)个点 开始森林里的树都是一个点,\(A,B\)支持合并(但树结构互不影响),\ ...

  4. Android开发菜单以及子菜单

    package com.example.androidtest; import android.app.Activity; import android.os.Bundle; import andro ...

  5. KC开发组官方网站

    KC开发组,简称KC,又叫KC Team,是Kong Che Team(空车开发组)的缩写 是由ordosyz(某乡村小学)的CSP2019退役OIer(信息学奥林匹克竞赛选手) 于2020年2月23 ...

  6. 关于eclipse 项目导入不了 maven依赖的解决办法

    1.首先确定你的项目是maven 项目 ,如果不是:项目右键Configure -->Convert to maven project. 2.在SVN导出的Maven项目,或以前不是用Maven ...

  7. Ubuntu系统测评

    首次使用ubuntun系统 华为云可以免费试用30天,嘻嘻,正好熟悉一下linux命令 1.登录 login: 先输入用户名:root 在输入密码:******** 这个是在配置云服务器的时候自己设置 ...

  8. 【剑指Offer】39:平衡二叉树

    题目描述: 输入一棵二叉树,判断该二叉树是否是平衡二叉树 题解:递归 /*最直接的做法,遍历每个结点,借助一个获取树深度的递归函数,根据该结点的左右子树高度差判断是否平衡,然后递归地对左右子树进行判断 ...

  9. BZOJ2190 SDOI2008 仪仗队 gcd,欧拉函数

    题意:求从左下角能看到的元素个数 引理:对点(x,y),连线(0,0)-(x,y),元素个数为gcd(x,y)-1(中间元素) 即要求gcd(x,y)=1 求gcd(x,y)=1的个数 转化为2 \s ...

  10. nodemon:无法将“nodemon”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

    主要问题还是你的终端(也就是你的nodemon命令执行的地方没有这个环境) 解决方法:设置全局的nodemon,在终端执行 npm install -g nodemon