![golang_real.jpg](https://upload-images.jianshu.io/upload_images/8207483-659e64f6f71b221d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

微服务已经 hot 了一段时间,自己作为 web 开发人员当然也不由自主想研究研究微服务,不过微服务的整个知识体系过于庞大,要掌握的概念和技术太多,一时有点吃不消。个人为了生计又没有大块时间去搞。不过还是难舍微服务,最近学习了 go 语言,想一块一块地吃掉微服务,先从 go 和容器入手。

我们知道微服务之间是需要相互调用和通讯的,一般会采用 RPC 来实现不同语言不用服务间的调用和通讯。那么我就先从 RPC 入手来学习微服务。

RPC 框架的特点

所谓的特点就是他能够满足那些需求,RPC 框架要实现以上目的需要满足以下需求

  • - 序列化(GOB)语言单一

  • - 上下文管理(超时控制)

  • - 拦截器(鉴权、统计和限流)

  • - 跨语言

  • - 服务注册

Call ID:由于客户端和服务端运行在不同进程,为了让客户端和服务端都了解调用了哪个函数,需要在两端维护一个函数到Call ID 的映射表,客户端根据表获取函数的 Call ID,发起请求,服务端根据 Call ID 来执行对应的函数,返回值给客户端。

序列化和反序列化:在本地调用时候函数是从栈中获取参数运行函数。而远程调用时候,如果需要在不同语言间相互调用函数,需要将参数进行序列化然后以字节流方式传递给服务端,服务端在反序列化来得到参数

网络传输:客户端和服务端间的调用往往是通过网络完成。只要能传递数据就行,与协议无关,可以使用 TCP 或 UDP。gRcp 使用的 HTTP2 。

什么是 RPC

>  RPC是指**远程过程**调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

为什么需要 RPC 呢

因为 RPC 是分布式系统中不同节点间流行的通讯方式,在互联网时代,RPC 和 IPC 一样成为不可或缺的基础构建。在 Go 语言的标准库也提供了简单的 RPC 的实现。

RPC 在服务间调用流程

我们通过上面图来看,这个流程比较清晰,也不难理解。

1. 调用客户端句柄,执行传送参数

2. 调用本地系统内核发送网络消息

3. 消息传送至远程机器

4. 服务器句柄得到消息并取得参数

5. 执行远程过程

6. 执行过程将结果返回给服务器句柄

7. 服务器句柄返回结果,调用远程系统内核

8. 消息传回本地主机

9. 客户句柄由内核接收消息

10. 客户接收句柄返回的数据

有了上面理论基础,我们基于理论来实现。

type RPCService struct{}

创建一个 RPCService 服务,随后将其进行注册

func (s *RPCService) Hello(request string, reply *string) error{  *reply = "Hello " + request  return nil}

- 函数必须是外部可以访问函数,函数名需要首字母大写

- 函数需要有两个参数

1. 第一个参数接收的参数

2. 第二个参数是返回给客户端的参数,而且需要是指针类型

- 函数还需要有一个 error 返回值

rpc.RegisterName("RPCService",new(RPCService))

注册rpc服务

listener, err := net.Listen("tcp",":1234")

创建 tcp 服务端口号为 1234 用于 rpc 服务。

    conn, err := listener.Accept()    if err != nil{        log.Fatal("Accept error:", err)    }    rpc.ServeConn(conn)

服务端完整代码

package mainimport(  // "fmt"  "log"  "net"  "net/rpc")type RPCService struct{}func (s *RPCService) Hello(request string, reply *string) error{  *reply = "Hello " + request  return nil}func main() {  rpc.RegisterName("RPCService",new(RPCService))  listener, err := net.Listen("tcp",":1234")  if err != nil{    log.Fatal("ListenTCP error:",err)  }  conn, err := listener.Accept()  if err != nil{    log.Fatal("Accept error:", err)  }  rpc.ServeConn(conn)}

客户端代码

client, err := rpc.Dial("tcp","localhost:1234")

客户端调用 RPC 服务,然后通过client.Call调用具体的RPC方法。

err = client.Call("RPCService.Hello","World",&reply)

在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别我们定义RPC方法的两个参数。

package mainimport(  "fmt"  "log"  "net/rpc")func main() {  client, err := rpc.Dial("tcp","localhost:1234")  if err != nil{    log.Fatal("dialing:", err)  }  var reply string  err = client.Call("RPCService.Hello","World",&reply)  if err != nil{    log.Fatal("call Hello method of RPCService:",err)  }  fmt.Println(reply)}

我们先后启动服务端和客户端就可以看到下面效果

Hello World

下面更加贴近实际来写一个基于 HTTP 的 RPC 服务,服务提供两个数四则运算。

rpcService := new(RPCService)    rpc.Register(rpcService)    rpc.HandleHTTP()

这里的注册方式略有不同,但是大同小异相信大家一看就懂。

服务端完整代码

package mainimport(  "errors"  "fmt"  "net/http"  "net/rpc")type Args struct{  A, B int}type Quotient struct{  Quo, Rem int}type RPCService intfunc (t *RPCService) Add(args *Args, reply *int) error{  *reply = args.A - args.B  return nil}func (t *RPCService) Multiply(args *Args, reply *int) error{  *reply = args.A * args.B  return nil}func (t *RPCService) Divide(args *Args, quo *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() {  rpcService := new(RPCService)  rpc.Register(rpcService)  rpc.HandleHTTP()    err := http.ListenAndServe(":1234",nil)  if err != nil{    fmt.Println(err.Error())  }}

客户端代码

```go

package mainimport(  "fmt"  "log"  "net/rpc"  "os")type Args struct{  A, B int}type Quotient struct{  Quo, Rem int}func main()  {  if len(os.Args) != 2{    fmt.Println("Usage: ", os.Args[0],"server")    os.Exit(1)  }  serverAddress := os.Args[1]  client, err := rpc.DialHTTP("tcp",serverAddress + ":1234")  if err != nil {    log.Fatal("dialing: ", err)  }  args := Args{17, 8}  var reply int  err = client.Call("RPCService.Add",args, &reply)  if err != nil{    log.Fatal("RPCService error: ", err)  }  fmt.Printf("RPCService: %d + %d = %d\n", args.A, args.B, &reply)  var quot Quotient  err = client.Call("RPCService.Divide",args, &quot)  if err != nil{    log.Fatal("RPCService error: ",err)  }  fmt.Printf("RPCService: %d/%d=%d remainder %d\n",args.A, args.B, quot.Quo,quot.Rem)}

```

```

RPCService: 17 + 8 = 824634312296RPCService: 17/8=2 remainder 1

```

其实在实际开发中我们还需要对其进行改造,例如让 rpc 请求可以获得一个 context 对象,其中包含用户信息等,然后可以对 rpc 进行超时处理。

#### JSONRPC

Go语言内置的 RPC 框架已经支持在 Http 协议上提供 RPC 服务。但是 Http 服务内置采用了 GOB 协议。编码不是 JSON 编码,不方便其他语言调用。不过 go 提供 JsonRPC 的 RPC 服务支持,我们来看一看怎么用代码实现。

服务端代码

```go

package mainimport(  "errors"  "fmt"  "net"  "net/rpc"  "net/rpc/jsonrpc"  // "os")type Args struct{  A, B int}type Quotient struct{  Quo, Rem int}type RPCService intfunc (t *RPCService) Multiply(args *Args, reply *int) error{  *reply = args.A * args.B  return nil}func (t *RPCService) Divide(args *Args, quo *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() {  rpcService := new(RPCService)  rpc.Register(rpcService)  tcpAddr, err := net.ResolveTCPAddr("tcp",":1234")  checkError(err)    listener, err := net.ListenTCP("tcp",tcpAddr)  checkError(err)  for{    conn, err := listener.Accept()    if err != nil{      continue    }    jsonrpc.ServeConn(conn)  }}func checkError(err error){  if err != nil{    fmt.Println("Fatal error ", err.Error())  }}

客户端代码

package mainimport(  "fmt"  "log"  "net/rpc/jsonrpc"  "os")type Args struct{  A, B int}type Quotient struct{  Quo, Rem int}func main()  {  if len(os.Args) != 2{    fmt.Println("Usage: ", os.Args[0],"server")    log.Fatal(1)  }  serverAddress := os.Args[1]  client, err := jsonrpc.Dial("tcp",serverAddress + ":1234")  if err != nil {    log.Fatal("dialing: ", err)  }  args := Args{17, 8}  var quot Quotient  err = client.Call("RPCService.Divide",args, &quot)  if err != nil{    log.Fatal("RPCService error: ",err)  }  fmt.Printf("RPCService: %d/%d=%d remainder %d\n",args.A, args.B, quot.Quo,quot.Rem)}

对原有RPC进行改造,添加一些辅助的功能

上下文管理

通过net/rpcj进行改造,可以添加一个上下文对象

func (t *RPCService) Add(c contex.Context,args *Args, reply *int) error{  *reply = args.A - args.B  return nil}

我们看一看这里有一个 Context 接口有些方法需要在 RPC 中进行实现,这里我们用 TCP 需要记录时间,通过 Context 可以获得一些额外而必要信息。

type Context interface{    ctx.Context    Now() time.Time    Seq() uint64    ServiceMethod() string    User() string}type rpcCtx struct{}func NewContext(c ctx.Context, u, m string, s uint64) Context{    }

超时控制

进行超时,通过超时控制避免过多请求阻塞,通过超时服务取消 rpc 请求。

func (t *RPCService) Timeout(c contex.Context, args *RPCTimeout, reply *struct{}) error{  log.Printf("Timeout: timeout=%s seq=%d\n",args.T, c.Seq())  time.Sleep(args.T)  return nil}

rpc wmi 服务不可用_golang 基础(5) RPC相关推荐

  1. rpc wmi 服务不可用_在Windows上修复“RPC服务器不可用”的方法

    关注奕奇科技,学习更多小妙招,工作效率加倍小妙招,值得收藏 RPC服务器,是指Remote Procedure Call Protocol,中文释义为(RFC-1831)远程过程调用协议:一种通过网络 ...

  2. rpc wmi 服务不可用_“RPC服务器不可用”使用WMI查询

    我有一个运行Server 2008 R2的Web服务器工作组,我试图管理一个检查所有这些磁盘空间的脚本.几个月前,我在设置服务器时设置了这一点,我相信它工作正常.现在我去检查,它给出了一个错误,说&q ...

  3. rpc服务器不可用 不显示桌面,rpc服务器不可用,教您rpc服务器不可用怎么解决

    用户只要选择"Internet 时间"中的立即更新,就能获得一个比较准确的系统时间.不过,不少用户在点击"立即更新"后,却遇到了"RPC服务器不可用& ...

  4. 打印提示rpc服务不可用

    提示页面: 在服务中开启以下服务 以下转载自:https://www.cnblogs.com/onemorepoint/p/7444420.html RPC服务不可用总结 A简单方法: 通过" ...

  5. pe修改rpc服务器不可用,ABBYY FineReader 12出现“RPC服务不可用”怎么办

    原标题:ABBYY FineReader 12出现"RPC服务不可用"怎么办 是一款OCR图文识别软件,可快速方便地将扫描纸质文档.PDF文件和数码相机的图像转换成可编辑.可搜索的 ...

  6. 修复2008r2 rpc服务器,windows server 2008R2 复制问题(RPC服务不可用)

    前段时间,有客户反馈有1台辅助域控复制有问题,报RPC服务不可用 打开事件查看器,发现很多event id 为6013事件,系统启动时间:45626584秒,大约是528天,系统运行时间超过了497天 ...

  7. “RPC服务不可用”的解决过程

    今天想练一下五笔,记得电脑里面有金山打字通只是没有装上.找到金山打字通开始安装,出现提示"RPC服务不可用",连着试了两次都是这样.就找到RPC服务打开.再试还是不行.看来只有问百 ...

  8. 深入理解 RPC : 基于 Python 自建分布式高并发 RPC 服务

    RPC(Remote Procedure Call)服务,也即远程过程调用,在互联网企业技术架构中占据了举足轻重的地位,尤其在当下微服务化逐步成为大中型分布式系统架构的主流背景下,RPC 更扮演了重要 ...

  9. 分布式微服务架构之SpringCloud基础篇

    一,微服务相关概念 1.微服务与分布式 微服务理论:一种架构风格,将项目的不同功能拆分成一个个模块,每个模块都能独立运行,彼此间通过远程调用实现功能. 分布式概念:很多建立在网络上的软件系统的集合. ...

最新文章

  1. Codeforces 894.D Ralph And His Tour in Binary Country
  2. BMP图片魔法师KeyGen
  3. mysql索引下沉_MySQL 5.6 索引条件下推优化
  4. 【转载】OpenStack Swift学习笔记
  5. Mysql5.7使用DTS增量同步数据到MaxCompute
  6. php 每天早上八点执行操作_php多进程单线程之phpcgi、phpfpm
  7. Linux符号连接的层数过多
  8. The 'manifest_version' key must be present and set to 2 (without quotes)
  9. laravel 除了主页 都是404 webconfig_通过 Laravel 创建一个 Vue 单页面应用(六)
  10. 中值滤波器处理椒盐噪声
  11. docker配置aria2
  12. 使用HttpClient发送短信
  13. php 开源系统(cms),30个很棒的PHP开源CMS内容管理系统
  14. 小试ESP8266(一) 一只电阻, 几条语句, 摆脱深度睡眠反复重启的困扰
  15. NYOJ - [第九届河南省程序设计大赛]宣传墙(DP)
  16. esxi能直通的显卡型号_七彩虹RTX SUPER祝融(火神)版显卡上手体验
  17. 阿里云CDN不止于加速:基于https国密算法构建安全数据传输链路
  18. Python自动化小技巧11——excel文件的文字内容筛选
  19. 京东活动+自动运行脚本+签到2021.05.26更新
  20. 如何在GitHub上创建自己的仓库?

热门文章

  1. 来个硬货——长文解读:基于业务场景的MySQL千万级大表优化
  2. 大剑无锋之如何评判一个算法的优劣【面试推荐】
  3. 当分区数量与reducer task数量不一致时,会怎么样。
  4. Spark _09资源调度和任务调度
  5. 【Java网络编程(四)】手写TCP聊天室——控制台版
  6. 看动画学算法之:栈stack
  7. Kafka Without ZooKeeper ---- 不使用zookeeper的kafka集群
  8. 工厂方法模式_工厂方法模式
  9. 3pc_three phase commit protocol协议理解
  10. jQuery——给元素添加父级的方法