本文先介绍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. 阻止a链接跳转的点击事件

    <a href="http://www.baidu.com" id="btn">按钮</a> <script> docume ...

  2. Mybatis常见面试题汇总

    Mybatis常见面试题汇总 最近在复习整理Mybatis的相关知识,针对面试中的典型问题,结合相关书籍和网上相关帖子,做如下整理. ================================= ...

  3. PAT (Basic Level) Practice (中文)1030 完美数列 (25 分) (有点意思)

    给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列. 现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列. 输入格 ...

  4. 题解 AT5632 【Sum of Two Integers】

    在幼儿园的时候,我们就学习过把一个数分成\(a\)与\(b\),我们只需要用计算机来模拟这个过程就可以了. 我们先从奇数开始看起,以\(5\)为例: 我们可以发现,\(5\)可以分成\(1\)和\(4 ...

  5. 爬格子呀--IEEE极限编程大赛留念

    10.14,坐标:电子科技大学 24h,不间断的编程,感觉还是很爽的. 排名一般,但是这是开始,未来还很远. 题目举例1: 广袤的非洲大草原上,狮子居住在一个个的网格里,他们的势力范围会以曼哈顿路程的 ...

  6. jQuery 判断页面上下滚动

    var t = 0, b = 0; $(window).scroll(function(){ t = $(this).scrollTop(); if(b < t){ console.log('向 ...

  7. LeetCode longest substring without repeating characters 题解 Hash表

    题目 Given a string, find the length of the longest substring without repeating characters. Example 1: ...

  8. 批量获取mysql数据库实例指定参数的值

    需求:需要对比所有mysql数据库实例上面的指定参数配置情况,同时需要需要能看到如ip,端口,master or slave,毕竟主和从参数不一样还是有可能的. 说明:必须要有个数据库存储所有是数据库 ...

  9. 19新生赛 谁更nb

    题目描述: 有一堆石子共有N个.syx xxh两个人轮流拿,syx先拿.每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获 胜.syx xxh都非常聪明,拿石子的过程中不会出现失误.给出N和K,问最后 ...

  10. TCL namespace

    命名空间可从Tcl 8.0版开始使用.引入命名空间之前,有一个全局范围.现在有了命名空间,我们可以分区全局范围. 创建命名空间: 结果:33 嵌套命名空间: 结果: test1 test2 导入命名空 ...