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时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结. 其实我也是一个新手,机缘巧合 ...
随机推荐
- ElementUI的Table-column_render-header自定义表头
ElementUI的Table表格,官方网站上提供了很多样式,但是在日常开发中还会碰到各种情况,显然官方提供的是不能满足需求的.那么,我们就根据自己的需求对table进行改造. 先丢出关于Table的 ...
- cli4适配移动端
1.首先在项目中安装以下依赖 npm install px2rem-loader --savenpm install amfe-flexible --savenpm install postcss-p ...
- jQuery---html方法和text方法
html方法和text方法 //html:innerHTML text:innerText console.log($("div").html());//<h3>我是标 ...
- vue(一)--监听事件
1.vue-on:监听事件: demo:点击按钮,number+1 v-on 还可以缩写为 @ 2.事件修饰符 .stop:等同于JavaScript中的event.stopPropagation() ...
- Vuejs开发环境的搭建
Windows系统上搭建VueJS开发环境 1.安装node.js:在node.js官网下载对应系统的msi包并安装 注:node的安装分全局和本地模式.一般情况下会以本地模式运行,包会被安装到和你的 ...
- Spring IoC详解
Spring IoC详解 1. 控制反转 控制反转是一种通过描述(XML或者注解)并通过第三方去产生或获取特定对象的方式.在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Depend ...
- H5 使用input标签上传图片给后台
html代码: <div class="hpk-showimg"> <!-- 营业执照 --> <div class="idcardup&q ...
- C++——指针3
指针作为函数参量 指针作为函数参量,以地址的方式传递数据,可以用来返回函数处理结果:实参是数组名时形参可以是指针. 题目:读入三个浮点数,将整数部分和小数部分分别输出 #include <ios ...
- linux - python:卸载
[root@test ~]# rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps ##强制删除已安装程序及其关联[root@test ~]# ...
- Java连载84-Collection的常用方法、迭代器
一.Collections的常用方法介绍 1.承接上次连载,先介绍几个简单的常用方法 package com.bjpowernode.java_learning; import java.util.* ...