一.简介
<blockquote class="kdocs-blockquote" style=""> 在上一节简单相识了微服务界说和优缺点之后,在使用微服务框架之前,须要起宰衡识一下RPC架构,通过RPC可以更形象相识微服务的工作流程 <blockquote class="kdocs-blockquote" style=""> RPC(Remote Procedure Call Protocol),是 长途过程调用的缩写,普通的说就是 调用远处的一个函数,与之相对应的是 本地函数调用,先来看一下本地函数调用:当写下如下代码的时间:
result := Add(1,2)
传入了1,2两个参数,调用了本地代码中的一个Add函数,得到result这个返回值,这时 参数, 返回值, 代码段都在一个进程空间内,这是 本地函数调用。 那有没有办法,可以大概调用一个 跨进程 (以是叫"长途",范例的事例,这个进程摆设在 另一台服务器 上)的函数呢? <blockquote class="kdocs-blockquote" style=""> 这就是 RPC重要实现的功能,也是 微服务的重要功能 使用微服务化的一个利益就是:
(1).不限定服务的提供方使用什么技能选型,可以大概实现公司跨团队的技能解耦
(2).每个服务都被封装成进程,相互"独立"
(3).使用微服务可以跨进程通讯
<blockquote class="kdocs-blockquote" style=""> RPC协议可以实现差别语言的直接相互调用,在互联网期间, RPC已经和 IPC(进程间通讯)一样成为一个不可或缺的根本构件 IPC: 进程间通讯
RPC:长途进通讯 —— 应用层协议(http协议同层),底层使用 TCP 实现
在golang中实现RPC非常简单,有封装好的官方库和一些第三方库提供支持,Go RPC可以使用tcp或http来转达数据,可以对要转达的数据使用多种范例的编解码方式。golang官方的net/rpc库使用encoding/gob举行编解码,支持tcp或http数据传输方式,由于其他语言不支持gob编解码方式,以是使用net/rpc库实现的RPC方法没办法举行跨语言调用。
golang官方还提供了net/rpc/jsonrpc库实现RPC方法,JSON RPC采取JSON举行数据编解码,因而支持跨语言调用,但现在的jsonrpc库是基于tcp协议实现的,临时不支持使用http举行数据传输。
除了golang官方提供的rpc库,尚有许多第三方库为在golang中实现RPC提供支持,大部门第三方rpc库的实现都是使用protobuf举行数据编解码,根据protobuf声明文件自动天生rpc方法界说与服务注册代码,在golang中可以很方便的举行rpc服务调用
二.net/rpc库实现长途调用
<blockquote class="kdocs-blockquote" style=""> 演示怎样使用golang官方的 net/rpc 库实现RPC方法,使用 http 作为RPC的载体,通过 net/http 包监听客户端毗连哀求。http基于tcp, 多一层封包和反复握手校验, 性能自然比直接用tcp实现网络传输要 差一些,以是RPC微服务中一样寻常使用的都是tcp (1).创建RPC微服务端
<blockquote class="kdocs-blockquote" style=""> 新建server/main.go - package main
- import (
- "fmt"
- "log"
- "net"
- "net/http"
- "net/rpc"
- "os"
- )
- // 定义类对象
- type World struct {
- }
- // 绑定类方法
- func (this *World) HelloWorld(req string, res *string) error {
- *res = req + " 你好!"
- return nil
- //return errors.New("未知的错误!")
- }
- // 绑定类方法
- func (this *World) Print(req string, res *string) error {
- *res = req + " this is Print!"
- return nil
- //return errors.New("未知的错误!")
- }
- func main() {
- // 1. 注册RPC服务
- rpc.Register(new(World)) // 注册rpc服务
- rpc.HandleHTTP() // 采用http协议作为rpc载体
- // 2. 设置监听
- lis, err := net.Listen("tcp", "127.0.0.1:8800")
- if err != nil {
- log.Fatalln("fatal error: ", err)
- }
- fmt.Fprintf(os.Stdout, "%s", "start connection")
- // 3. 建立链接
- http.Serve(lis, nil)
- }
复制代码 注意:以上World结构体的方法方法必须满意Go语言的RPC规则:
- 方法只能有两个可序列化的参数,此中第二个参数是指针范例,参数的范例不能是channel(通道)、complex(复数范例)、func(函数),由于它们不能举行 序列化
- 方法要返回一个error范例,同时必须是公开的方法
(2). 创建RPC客户端
<blockquote class="kdocs-blockquote" style=""> 客户端可以是 go web 也可以是一个 go应用,新建client/main.go - package main
- import (
- "fmt"
- "net/rpc"
- )
- func main() {
- // 1. 用 rpc 链接服务器 --Dial()
- conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8800")
- if err != nil {
- fmt.Println("Dial err:", err)
- return
- }
- defer conn.Close()
- // 2. 调用远程函数
- var reply1 string // 接受返回值 --- 传出参数
- err1 := conn.Call("World.HelloWorld", "张三", &reply1)
- if err1 != nil {
- fmt.Println("Call:", err1)
- return
- }
- fmt.Println(reply1)
- var reply2 string // 接受返回值 --- 传出参数
- err2 := conn.Call("World.Print", "李四", &reply2)
- if err2 != nil {
- fmt.Println("Call:", err2)
- return
- }
- fmt.Println(reply2)
- }
复制代码 (1).创建RPC微服务端
<blockquote class="kdocs-blockquote" style=""> 新建server/main.go - package main
- import (
- "fmt"
- "net"
- "net/rpc"
- )
- // 定义类对象
- type World struct {}
- // 绑定类方法
- func (this *World) HelloWorld(req string, res *string) error {
- *res = req + " 你好!"
- return nil
- }
- func main() {
- // 1. 注册RPC服务
- err := rpc.RegisterName("hello", new(World))
- if err != nil {
- fmt.Println("注册 rpc 服务失败!", err)
- return
- }
- // 2. 设置监听
- listener, err := net.Listen("tcp", "127.0.0.1:8800")
- if err != nil {
- fmt.Println("net.Listen err:", err)
- return
- }
- defer listener.Close()
- fmt.Println("开始监听 ...")
- // 3. 建立链接
- for {
- //接收连接
- conn, err := listener.Accept()
- if err != nil {
- fmt.Println("Accept() err:", err)
- return
- }
- // 4. 绑定服务
- go rpc.ServeConn(conn)
- }
- }
复制代码注意:以上World结构体的方法方法必须满意Go语言的RPC规则:
- 方法只能有两个可序列化的参数,此中第二个参数是指针范例,参数的范例不能是channel(通道)、complex(复数范例)、func(函数),由于它们不能举行 序列化
- 方法要返回一个error范例,同时必须是公开的方法
(2). 创建RPC客户端
<blockquote class="kdocs-blockquote" style=""> 新建client/main.go - package main
- import (
- "fmt"
- "net/rpc"
- )
- func main() {
- // 1. 用 rpc 链接服务器 --Dial()
- conn, err := rpc.Dial("tcp", "127.0.0.1:8800")
- if err != nil {
- fmt.Println("Dial err:", err)
- return
- }
- defer conn.Close()
- // 2. 调用远程函数
- var reply string // 接受返回值 --- 传出参数
- err = conn.Call("hello.HelloWorld", "张三", &reply)
- if err != nil {
- fmt.Println("Call:", err)
- return
- }
- fmt.Println(reply)
- }
复制代码 <blockquote class="kdocs-blockquote" style=""> 阐明:
首选是通过rpc.Dial拨号RPC服务,然后通过client.Call调用详细的RPC方法,在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别界说RPC方法的两个参数 三.使用tcp作为RPC的载体实现长途调用详细案例
案例1.简单使用
<blockquote class="kdocs-blockquote" style=""> 1.创建一个hello微服务端,编写微服务端RPC代码,完成后启动该微服务端
2.创建一个hello客户端,编写客户端RPC代码,完成后启动该客户端,访问微服务端RPC功能,并返回相干数据 (1).创建hello微服务端
<blockquote class="kdocs-blockquote" style=""> 创建mirco/server/hello/main.go文件,并编写代码,代码下所示: - package main
- import (
- "fmt"
- "net"
- "net/rpc"
- )
- //rpc服务端
- //定义一个远程调用的结构体,并创建一个远程调用的函数,函数一般是放在结构体中的
- type Hello struct {
- }
- /*
- 说明:
- 1、方法只能有两个可序列化的参数,其中第二个参数是指针类型
- req 表示获取客户端传过来的数据
- res 表示给客户端返回数据
- 2、方法要返回一个error类型,同时必须是公开的方法
- 3、req和res的类型不能是:channel(通道)、func(函数),因为以上类型均不能进行 序列化
- */
- func (this Hello) SayHello(req string, res *string) error {
- fmt.Println("请求的参数:", req)
- //设置返回的数据
- *res = "你好" + req
- return nil
- }
- func main() {
- //1、 注册RPC服务
- //hello: rpc服务名称
- err1 := rpc.RegisterName("hello", new(Hello))
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、监听端口
- listen, err2 := net.Listen("tcp", "127.0.0.1:8080")
- if err2 != nil {
- fmt.Println(err2)
- }
- //3、应用退出的时候关闭监听端口
- defer listen.Close()
- for { // for 循环, 一直进行连接,每个客户端都可以连接
- fmt.Println("开始创建连接")
- //4、建立连接
- conn, err3 := listen.Accept()
- if err3 != nil {
- fmt.Println(err3)
- }
- //5、绑定服务
- rpc.ServeConn(conn)
- }
- }
复制代码 (2).创建hello客户端
<blockquote class="kdocs-blockquote" style=""> 创建mirco/client/hello/main.go文件,并编写代码,代码下所示: - package main
- import (
- "fmt"
- "net/rpc"
- )
- //rpc服务端
- func main() {
- //1、用 rpc.Dial和rpc微服务端建立连接
- conn, err1 := rpc.Dial("tcp", "127.0.0.1:8080")
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、当客户端退出的时候关闭连接
- defer conn.Close()
- //3、调用远程函数
- //微服务端返回的数据
- var reply string
- /*
- 1、第一个参数: hello.SayHello,hello 表示服务名称 SayHello 方法名称
- 2、第二个参数: 给服务端的req传递数据
- 3、第三个参数: 需要传入地址,获取微服务端返回的数据
- */
- err2 := conn.Call("hello.SayHello", "我是客户端", &reply)
- if err2 != nil {
- fmt.Println(err2)
- }
- //4、获取微服务返回的数据
- fmt.Println(reply)
- }
复制代码 (3).启动微服务端,以及客户端访问
启动微服务端
启动客户端
案例2.模仿实现一个goods的微服务,增长商品 获取商品功能
<blockquote class="kdocs-blockquote" style="text-align:left;"> 1.创建一个goods微服务端,编写微服务端RPC代码,增长函数: 增长商品函数,获取商品函数,完成后启动该微服务端
2.创建一个goods客户端,编写客户端RPC代码,完成后启动该客户端,访问微服务端RPC功能,并返回相干数据 (1).创建goods微服务端
<blockquote class="kdocs-blockquote" style="text-align:left;"> 创建mirco/server/goods/main.go文件,并编写代码,代码下所示: - package main
- import (
- "fmt"
- "net"
- "net/rpc"
- )
- // goods微服务:服务端,传入struct,增加商品,获取商品
- //创建远程调用的函数,函数一般是放在结构体里面
- type Goods struct{}
- //AddGoods参数对应的结构体
- //增加商品请求参数结构体
- type AddGoodsReq struct {
- Id int
- Title string
- Price float32
- Content string
- }
- //增加商品返回结构体
- type AddGoodsRes struct {
- Success bool
- Message string
- }
- //GetGoods参数对应的结构体
- //获取商品请求结构体
- type GetGoodsReq struct {
- Id int
- }
- //获取商品返回结构体
- type GetGoodsRes struct {
- Id int
- Title string
- Price float32
- Content string
- }
- /*
- 说明:
- 1、方法只能有两个可序列化的参数,其中第二个参数是指针类型
- req 表示获取客户端传过来的数据
- res 表示给客户端返回数据
- 2、方法要返回一个error类型,同时必须是公开的方法
- 3、req和res的类型不能是:channel(通道)、func(函数),因为以上类型均不能进行 序列化
- */
- //增加商品函数
- func (this Goods) AddGoods(req AddGoodsReq, res *AddGoodsRes) error {
- //1、执行增加 模拟
- fmt.Printf("%#v\n", req)
- *res = AddGoodsRes{
- Success: true, //根据增加结果,返回状态
- Message: "增加商品成功",
- }
- return nil
- }
- //获取商品函数
- func (this Goods) GetGoods(req GetGoodsReq, res *GetGoodsRes) error {
- //1、执行获取商品 模拟
- fmt.Printf("%#v\n", req)
- //2、返回获取的结果
- *res = GetGoodsRes{
- Id: 12, //商品id
- Title: "服务器获取的数据",
- Price: 24.5,
- Content: "我是服务器数据库获取的内容",
- }
- return nil
- }
- func main() {
- //1、 注册RPC服务
- //goods: rpc服务名称
- err1 := rpc.RegisterName("goods", new(Goods))
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、监听端口
- listen, err2 := net.Listen("tcp", "127.0.0.1:8080")
- if err2 != nil {
- fmt.Println(err2)
- }
- //3、应用退出的时候关闭监听端口
- defer listen.Close()
- for { // for 循环, 一直进行连接,每个客户端都可以连接
- fmt.Println("准备建立连接")
- //4、建立连接
- conn, err3 := listen.Accept()
- if err3 != nil {
- fmt.Println(err3)
- }
- //5、绑定服务
- rpc.ServeConn(conn)
- }
- }
复制代码 (2).创建goods客户端
<blockquote class="kdocs-blockquote" style="text-align:left;"> 创建mirco/client/goods/main.go文件,并编写代码,代码下所示: - package main
- import (
- "fmt"
- "net/rpc"
- )
- //AddGoods参数对应的结构体
- //增加商品请求参数结构体
- type AddGoodsReq struct {
- Id int
- Title string
- Price float32
- Content string
- }
- //增加商品返回结构体
- type AddGoodsRes struct {
- Success bool
- Message string
- }
- //GetGoods参数对应的结构体
- //获取商品请求结构体
- type GetGoodsReq struct {
- Id int
- }
- //获取商品返回结构体
- type GetGoodsRes struct {
- Id int
- Title string
- Price float32
- Content string
- }
- func main() {
- //1、用 rpc.Dial和rpc微服务端建立连接
- conn, err1 := rpc.Dial("tcp", "127.0.0.1:8080")
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、当客户端退出的时候关闭连接
- defer conn.Close()
- //3、调用远程函数
- //微服务端返回的数据
- var reply AddGoodsRes
- /*
- 1、第一个参数: goods.AddGoods,goods 表示服务名称 AddGoods 方法名称
- 2、第二个参数: 给服务端的req传递数据
- 3、第三个参数: 需要传入地址,获取微服务端返回的数据
- */
- err2 := conn.Call("goods.AddGoods", AddGoodsReq{
- Id: 10,
- Title: "商品标题",
- Price: 23.5,
- Content: "商品详情",
- }, &reply)
- if err2 != nil {
- fmt.Println(err1)
- }
- //4、获取微服务返回的数据
- fmt.Println("%#v\n", reply)
- // 5、 调用远程GetGoods函数
- var goodsData GetGoodsRes
- err3 := conn.Call("goods.GetGoods", GetGoodsReq{
- Id: 12,
- }, &goodsData)
- if err3 != nil {
- fmt.Println(err3)
- }
- //6、获取微服务返回的数据
- fmt.Printf("%#v", goodsData)
- }
复制代码 (3).启动微服务端,以及客户端访问
启动微服务端
启动客户端
四.net/rpc/jsonrpc库以及RPC跨语言
<blockquote class="kdocs-blockquote" style=""> 标准库的RPC默认采取Go语言特有的 gob编码, 没法实现跨语言调用,golang官方还提供了 net/rpc/jsonrpc库实现RPC方法,JSON RPC采 用JSON举行数据编解码,因而 支持跨语言调用, 但现在的jsonrpc库是 基于tcp协议 实现的,临时不支持使用http举行数据传输 <blockquote class="kdocs-blockquote" style=""> nc是 netcat的简写,是一个功能强大的网络工具,有着网络界的瑞士军刀美誉,nc下令的 重要作用如下:
- 实现恣意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口
- 端口的扫描,nc可以作为client发起TCP或UDP毗连
<blockquote class="kdocs-blockquote" style=""> centos中如果找不到nc下令可以使用 yum install -y nc 安装 使用nc作为微服务server端吸收客户端数据
- nc -l 192.XXX.XXX.XXX 8080
复制代码 nc作为微服务server端开启:
 客户端哀求和上面案例划一,也可以参考下面案例
<blockquote class="kdocs-blockquote" style=""> 上面解说了使用 net/rpc 实现RPC的过程,但是没办法在其他语言中调用上面例子实现的RPC方法。以是接下来使用 net/rpc/jsonrpc 库实现RPC方法,此方式实现的 RPC方法支持跨语言调用 <blockquote class="kdocs-blockquote" style=""> 使用 net/rpc/jsonrpc 库实现RPC方法:
和rpc微服务端区别: 在 5. 绑定服务步调中使用 rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) - package main
- import (
- "fmt"
- "net"
- "net/rpc"
- "net/rpc/jsonrpc"
- )
- //rpc服务端
- //定义一个远程调用的结构体,并创建一个远程调用的函数,函数一般是放在结构体中的
- type Hello struct {
- }
- /*
- 说明:
- 1、方法只能有两个可序列化的参数,其中第二个参数是指针类型
- req 表示获取客户端传过来的数据
- res 表示给客户端返回数据
- 2、方法要返回一个error类型,同时必须是公开的方法
- 3、req和res的类型不能是:channel(通道)、func(函数),因为以上类型均不能进行 序列化
- */
- func (this Hello) SayHello(req string, res *string) error {
- fmt.Println("请求的参数:", req)
- //设置返回的数据
- *res = "你好" + req
- return nil
- }
- func main() {
- //1、 注册RPC服务
- //hello: rpc服务名称
- err1 := rpc.RegisterName("hello", new(Hello))
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、监听端口
- listen, err2 := net.Listen("tcp", "127.0.0.1:8080")
- if err2 != nil {
- fmt.Println(err2)
- }
- //3、应用退出的时候关闭监听端口
- defer listen.Close()
- for { // for 循环, 一直进行连接,每个客户端都可以连接
- fmt.Println("开始创建连接")
- //4、建立连接
- conn, err3 := listen.Accept()
- if err3 != nil {
- fmt.Println(err3)
- }
- //5、绑定服务
- //rpc.ServeConn(conn)
- // 5. 绑定服务
- /*
- jsonrpc和默认rpc的区别:
- 以前rpc.ServeConn(conn)绑定服务
- jsonrpc中通过rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
- */
- rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
- }
- }
复制代码 <blockquote class="kdocs-blockquote" style=""> 代码中最大的厘革是用 rpc.ServeCodec函数更换了 rpc.ServeConn函数,传入的参数是针对服务端的json编解码器 - package main
- import (
- "fmt"
- "net"
- "net/rpc"
- "net/rpc/jsonrpc"
- )
- //rpc服务端
- /*
- 把默认的rpc 改为jsonrpc
- 1、rpc.Dial需要调换成net.Dial
- 2、增加建立基于json编解码的rpc服务 client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
- 3、conn.Call 需要改为client.Call
- */
- func main() {
- //1、用 net.Dial和rpc微服务端建立连接
- conn, err1 := net.Dial("tcp", "127.0.0.1:8080")
- if err1 != nil {
- fmt.Println(err1)
- }
- //2、当客户端退出的时候关闭连接
- defer conn.Close()
- //建立基于json编解码的rpc服务
- client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
- //3、调用远程函数
- //微服务端返回的数据
- var reply string
- /*
- 1、第一个参数: hello.SayHello,hello 表示服务名称 SayHello 方法名称
- 2、第二个参数: 给服务端的req传递数据
- 3、第三个参数: 需要传入地址,获取微服务端返回的数据
- */
- err2 := client.Call("hello.SayHello", "张三", &reply)
- if err2 != nil {
- fmt.Println(err2)
- }
- //4、获取微服务返回的数据
- fmt.Println(reply)
- }
复制代码 <blockquote class="kdocs-blockquote" style=""> 先手工调用 net.Dial函数创建TCP链接,然后基于该链接创建针对客户端的json编解码器 启动微服务端
启动客户端
<blockquote class="kdocs-blockquote" style=""> 以PHP跨语言调用RPC微服务为案例 PHP代码
- <?php
- class JsonRPC {
- private $conn;
- function __construct($host, $port) {
- $this->conn = fsockopen($host, $port, $errno, $errstr, 3);
- if (!$this->conn) {
- return false;
- }
- }
- public function Call($method, $params) {
- if (!$this->conn) {
- return false;
- }
- $err = fwrite($this->conn, json_encode(array(
- 'method' => $method,
- 'params' => array($params),
- 'id' => 0,
- ))."\n");
- if ($err === false) {
- return false;
- }
- stream_set_timeout($this->conn, 0, 3000);
- $line = fgets($this->conn);
- if ($line === false) {
- return NULL;
- }
- return json_decode($line,true);
- }
- }
- $client = new JsonRPC("127.0.0.1", 8080);
- $args = "this is php aaa";
- $r = $client->Call("Hello.SayHello", $args);
- print_r($r);
- ?>
复制代码 服务端启动和上面微服务端启动划一,php端访问,效果如下:
<blockquote class="kdocs-blockquote" style=""> 后期使用微服务框架 GRPC 和 Go-Micro的时间,都是使用 框架封装好的服务和客户端,接下来通过一个简单的示例演示一下 怎样封装,以此来明确 封装的原理,上面的代码服务名都是写死的,不敷机动(轻易写错),这里对RPC的服务端和客户端再次举行一次封装,来 屏蔽掉服务名,详细代码如下: 服务端封装
<blockquote class="kdocs-blockquote" style=""> 新建server/models/tools - package models
- import "net/rpc"
- var serverName = "HelloService"
- type RPCInterface interface {
- HelloWorld(string, *string) error
- }
- // 调用该方法时, 需要给 i 传参, 参数应该是 实现了 HelloWorld 方法的类对象!
- func RegisterService(i RPCInterface) {
- rpc.RegisterName(serverName, i)
- }
复制代码 封装之后的服务端实现如下:
- package main
- import (
- "fmt"
- "net"
- "net/rpc"
- "net/rpc/jsonrpc"
- "server/models"
- )
- // 定义类对象
- type World struct {}
- // 绑定类方法
- func (this *World) HelloWorld(req string, res *string) error {
- fmt.Println(req)
- *res = req + " 你好!"
- return nil
- //return errors.New("未知的错误!")
- }
- func main() {
- //注册rpc服务 维护一个hash表,key值是服务名称,value值是服务的地址
- // rpc.RegisterName("HelloService", new(World))
- models.RegisterService(new(World))
- //设置监听
- listener, err := net.Listen("tcp", ":8080")
- if err != nil {
- panic(err)
- }
- for {
- //接收连接
- conn, err := listener.Accept()
- if err != nil {
- panic(err)
- }
- //给当前连接提供针对json格式的rpc服务
- go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
- }
- }
复制代码 客户端封装
<blockquote class="kdocs-blockquote" style=""> 新建client/models/tools - package models
- import (
- "fmt"
- "net"
- "net/rpc"
- "net/rpc/jsonrpc"
- )
- var serverName = "HelloService"
- type RPCClient struct {
- Client *rpc.Client
- Conn net.Conn
- }
- func NewRpcClient(addr string) RPCClient {
- conn, err := net.Dial("tcp", addr)
- if err != nil {
- fmt.Println("链接服务器失败")
- return RPCClient{}
- }
- //套接字和rpc服务绑定
- client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
- return RPCClient{Client: client, Conn: conn}
- }
- func (this *RPCClient) CallFunc(req string, resp *string) error {
- return this.Client.Call(serverName+".HelloWorld", req, resp)
- }
复制代码 封装之后客户端实现
- package main
- import (
- "client/models"
- "fmt"
- )
- func main() {
- //建立tcp连接
- client := models.NewRpcClient("127.0.0.1:8080")
- //关闭连接
- defer client.Conn.Close()
- var reply string // 接受返回值 --- 传出参数
- err := client.CallFunc("this is client", &reply)
- if err != nil {
- fmt.Println("Call:", err)
- return
- }
- fmt.Println(reply)
- }
复制代码 [上一节][golang 微服务] 1.单体式架构以及微服务架构先容
[下一节][golang 微服务] 3. ProtoBuf熟悉与使用
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |