【初识够go-rpc】

RPC

1.RPC简介

1.1 什么是rpc协议

  • 什么是rpc?

    • rpc (remote procedure call protocol) 就是远程过程调用时一个计算机远程调用
    • rpc底层是通过http协议来实现的
  • rpc如何实现的?

    • 想调用本地函数一样,区调用远端 函数。
      • 通过rpc协议,传递:函数名称、函数参数、调用yuan
  • 为什么微服务要使用rpc?

    • 每个服务都被封装为进程。彼此独立
    • 进程和进程之间,可以使用不同的语言实现
image-20210625175418390

1.2 网络模型

osi 7层协议

  • 物理层
  • 数据链路层
  • 网络层
  • 传输层
  • 会话层
  • 表示层
  • 应用层

tcp-ip4层

  • 链路层
  • 网络层
  • 传输层:tcp\udp
  • 应用层:rpc\ws\http

2.RPC框架对比

image-20210624110040073

3.RPC入门使用

go语言的一般性的socket网络通信

# server端 伪代码
listener := net.Listen() # listener
conn := listener.Accept() # conn
conn.Read()
conn.Write()
defer conn.Close()
defer listener.Close()
# client端 伪代码
conn := net.Dial()
conn.Write()
conn.Read()
defer conn.Close()
  • RPC的使用步骤

    • server端

      • 注册rpc服务对象。给对象绑定方法(1.注册类、2.绑定类方法)

        rpc.RegisterName("服务名",回调对象)
        
      • 去创建一个监听器

        listener,err := net.Listen()
        
      • 建立连接

        conn,err := listener.Accept()
        
      • 将连接绑定rpc服务

        rpc.ServerConn(conn)
        
    • client端

      • 用rpc先连接服务器

        conn,err := rpc.Dial()
        
      • 传递参数调用远程函数

        conn.Call("服务名.方法名",传入参数,传出参数)
        
  • rpc相关函数

    • 注册rpc服务

      func(server *server)RegisterName(name string,recv interface{})error
      // name 服务名称 string
      // recv	rpc对象 interface{} 
      
      # 该对象绑定方法必须满足如下条件
      # 1.方法必须是导出的,包外可见,首字母大写
      # 2.方法必须有两个参数,都是导出类型,内建类型
      # 3.方法的第二个参数必须是指针(传出参数必须是指针)
      # 4.方法只有一个error接口类型的返回值
      
      //示例
      type World struct(){
      }
      func(this *world)HelloWorld(name string,resp *string)error{
      }
      rpc.RegisterName("服务名",new(World))
      
    • 绑定rpc服务

      func (server *Server) ServeConn(conn io.ReadWriteCloser)
      // conn 是成功建立好连接的socket
      
    • 调用远程函数

      func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error
      //serviceMethod 服务吗.方法名
      //args 函数的参数 传入参数
      //reply 传出参数 定义一个变量 取地址 &变量
      

4.RPC编码实现

client

package main

import (
	"fmt"
	"net/rpc"
)

func main(){
	// 1.用rpc连接服务器
	conn,err := rpc.Dial("tcp","127.0.0.1:8800")
	if err != nil{
		fmt.Println("conn error")
		return
	}
	defer conn.Close()
	// 2.调用远程方法
	var reply string
	err = conn.Call("hello.HelloWorld","libai",&reply)
	if err != nil{
		fmt.Println("Call error")
		return
	}
	fmt.Println(reply)
}

server

package main

import (
	"fmt"
	"net"
	"net/rpc"
)

//定义类对象
type World struct {
}

//绑定类方法
func (self *World) HelloWorld(name string, resp *string) error {
	*resp = name + ",你好!"
	return nil
}
func main() {
	// 1.注册rpc服务,绑定对象方法
	err := rpc.RegisterName("hello", new(World))
	if err != nil {
		fmt.Println("Register error", err)
	}

	// 2.设置监听
	listener,err := net.Listen("tcp","127.0.0.1:8800")
	if err != nil{
		fmt.Println("Listen error")
		return
	}
	defer listener.Close()
	fmt.Println("开始监听!")
	//3.建立连接
	conn,err := listener.Accept()
	if err != nil{
		fmt.Println("Accept error")
		return
	}
	defer conn.Close()

	//4.绑定服务
	rpc.ServeConn(conn)

}

5.JSON-rpc

  • 使用nc -l 127.0.0.1 8800来充当服务器

    • 使用rpc客户端

      # client
      conn,err := rpc.Dial("tcp","127.0.0.1:8800")
      
      # server
      huhao@huhaodeMacBook-Pro ~ % nc -l 127.0.0.1 8800
      ServiceMethod
                   Seq��hello.HelloWorld
                                       libai
      
    • 使用jsonrpc客户端后

      # client
      conn,err := jsonrpc.Dial("tcp","127.0.0.1:8800")
      
      # server
      huhao@huhaodeMacBook-Pro ~ % nc -l 127.0.0.1 8800
      {"method":"hello.HelloWorld","params":["libai"],"id":0}
      

6.protobuf

google开发的

image-20210628152351249

// .proto
// 默认是proto2
syntax = "proto3";

// 指定所在的包
package pb;

// 枚举
enum Week {
    Monday = 0;
    Tuesday = 1;
}

// 定义消息
message Student {
    int32 age = 1;
    string name = 2;
    People p = 3;
    repeated int32 score = 4; //数组
    //枚举
    Week w = 5;
    //联合体
    oneof data {
        string teacher = 6;
        string class = 7;
    }
}

// 消息可以嵌套
message People{
    int32 weight = 1;
}

7.编译protobuf

# protoc等安装
go get -u -v github.com/golang/protobuf/proto
go get google.golang.org/grpc
go get github.com/golang/protobuf/protoc-gen-go
  • go中编译编译

    protoc --go__out=./ *.proto

  • GO的RPC必须满足四个条件

    • 结构体字段要大写,字段访问要大写
    • 函数名首字母大写
    • 函数第一个参数代表的是接收参数,第二个参数是返回给客户端的参数,必须是指针类型
    • 函数必须有一个返回值error

8.GRPC简单示例

示例1:求长方形的面积和周长

  • client

    // server
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"net/rpc"
    )
    
    //服务端 求长方形的面积和周长
    
    //声明一个矩形对象
    type Rect struct {
    }
    
    //声明参数的结构体
    type Params struct {
    	//长和宽
    	Width, Height int
    }
    
    
    //定义求面积的方法
    func (r *Rect) Area(p Params, ret *int) error {
    	*ret = p.Width * p.Height
    	fmt.Println(ret)
    	fmt.Println(*ret)
    	fmt.Println(&ret)
    	return nil
    }
    
    //定义求周长的方法
    func (r *Rect) Perimeter(p Params, ret *int) error {
    	*ret = (p.Width + p.Height) * 2
    	fmt.Println(ret)
    	fmt.Println(*ret)
    	fmt.Println(&ret)
    	return nil
    }
    
    func main() {
    	//1.注册服务
    	rect := new(Rect)
    	rpc.Register(rect)
    	//2.把服务绑定到http协议上
    	rpc.HandleHTTP()
    	//3.等待客户端调用
    	err := http.ListenAndServe(":8080", nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    }
    
  • client

    //clientpackage mainimport (	"fmt"	"log"	"net/rpc")//声明参数的结构体type Params struct {	//长和宽	Width, Height int}//调用服务func main(){	//1.连接远程的rpc服务	rp,err := rpc.DialHTTP("tcp","127.0.0.1:8080")	if err != nil{		log.Fatal(err)	}	//2.调用远程方法	ret := 0	//求面积	err2 := rp.Call("Rect.Area",Params{50,100},&ret)	fmt.Println("面积:",ret)	if err2 != nil{		log.Fatal(err2)	}	//求周长	err3 := rp.Call("Rect.Perimeter",Params{50,100},&ret)	fmt.Println("周长:",ret)	if err3 != nil{		log.Fatal(err3)	}}
    

示例2:除法求结果

  • server

    package mainimport (	"log"	"net/http"	"net/rpc")//声明一个除法的结构体type Division struct {}//声明除法的两个变量type Params struct {	Data1 int	//除数	Data2 int	//被除数}//定义一个求除法结果的方法func (d *Division)Get_devision(p Params,ret *int)error{	*ret = p.Data1/p.Data2	return nil}func main(){	//建立一个rpc连接	rect := new(Division)	rpc.Register(rect)	rpc.HandleHTTP()	err := http.ListenAndServe(":8081",nil)	if err != nil {		log.Fatal(err)	}	//监听一个端口}
    
  • client

    package mainimport (	"fmt"	"log"	"net/rpc")//声明一个参数的结构体type Params struct {	Data1 int	//除数	Data2 int	//被除数}func main(){	//1.连接远程的rpc服务	rp,err := rpc.DialHTTP("tcp","127.0.0.1:8081")	if err != nil{		log.Fatal(err)	}	//2.调用远程方法	ret := 0	err2 := rp.Call("Division.Get_devision",Params{10,5},&ret)	if err2 != nil{		log.Fatal(err2)	}	fmt.Println("除法的结果:",ret)}
    

9.rpc调用流程

对内rpc,对外rest

image-20210625154820092

10.go micro框架

// go get -u -v github.com/micro/protoc-gen-micro// go get -u -v github.com/micro/micro// go get -u -v github.com/micro/go-micro

代码结构:

image-20210628184924157

10.1 服务发现

是微服务必须的技术

image-20210628185739723

image-20210628185859517
  • 服务发现也可以看作一个服务,是给服务提供服务的

10.2 服务发现的种类

  • consul:常用语go-micro
  • mdns:go-micro默认的
  • etcd:k8s内嵌的服务发现
  • zookeeper:java的服务发现

10.3 consul 使用

image-20210628190503048

10.4 consul安装

image-20210628190606468

image-20210628190926775

10.5 consul命令

image-20210628191549857

10.6 consul简单使用

image-20210628191635165

10.7 server模式启动

image-20210628191712734

11.micro安装

image-20210629112643655

12.micro使用

image-20210630102936286

image-20210630103244060

posted @ 2021-09-28 14:35  GokuBlog  阅读(191)  评论(0编辑  收藏  举报