ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Go微服务三 Go-RPC

2022-03-20 14:03:55  阅读:161  来源: 互联网

标签:服务 nil err rpc ret go RPC Go conn


RPC

1. 简介 什么是RPC?

  • 远程过程调用(Remote Procedure Call, RPC)。计算机的一个通信协议

  • 该协议允许一台计算机的程序调用另一台计算机的程序。

  • 如果涉及的软件采用的面向对象编程,远程调用也可以叫做远程调用或远程方法调用

2. Go实现RPC

2.1 如何实现

  • go使用官方提供的nei/rpc包使用encoding/gob进行编码,支持tcp和http数据传输,由于其他语言暂不支持gob编码方式,所以go的rpc只支持go开发的服务与客户端之间的交互
  • 但是官方还提供了net/rpc/jsonrpc库实现rpc方法,jsonrpc采用的是json格式进行数据编码,因此支持跨语言调用,目前jsonrpc是基于tcp协议实现的,暂不支持http传输方式

2.2 Go写RPC,必须符合的四个条件,不然使用不了

  • 结构体字段首字母要大写,可以别人调用
  • 函数名必须首字母大写
  • 函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型
  • 函数还必须有一个返回值error

2.3 RPC调用流程

  • 微服务架构下数据交互一般是对内RPC,对外REST

  • 将应用按照业务边界拆分成各个服务,降低耦合度

  • 各个服务单独运行,通过网络调用

2.4 RPC服务端

  • 服务端接收到的数据包括调用的函数名、参数列表、还有一个返回值error
  • 服务端核心功能包括维护函数map、接收客户端传来的参数解析处理相应的逻辑、函数的返回值打包,传给客户端

2.5 RPC - 实现 支持tcp和http 但是不支持跨语言

Example:

server.go

package main

import (
	"log"
	"net/http"
	"net/rpc"
)

type Params struct {
	Width, Height int
}

type Rect struct{}

// RPC服务端方法,求矩形面积
func (r *Rect) Area(p Params, ret *int) error {
	*ret = p.Height * p.Width
	return nil
}

// 周长
func (r *Rect) Perimeter(p Params, ret *int) error {
	*ret = (p.Height + p.Width) * 2
	return nil
}

// 主函数
func main() {
	// 1.注册服务
	rect := new(Rect)
	// 注册一个rect的服务
	rpc.Register(rect)
	// 2.服务处理绑定到http协议上
	rpc.HandleHTTP()
	// 3.监听服务
	err := http.ListenAndServe(":8095", nil)
	if err != nil {
		log.Panicln(err)
	}
}

client.go

package main

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

// 传的参数
type Param struct {
	Width, Height int
}

func main() {
	// 1.连接远程rpc服务
	conn, err := rpc.DialHTTP("tcp", ":8095")
	if err != nil {
		log.Fatal(err)
	}
	// 2.调用方法
	// 面积
	ret := 0
	err2 := conn.Call("Rect.Area", Param{50, 100}, &ret)
	if err2 != nil {
		log.Fatal(err2)
	}
	fmt.Println("面积:", ret)
	// 周长
	err3 := conn.Call("Rect.Perimeter", Param{50, 100}, &ret)
	if err3 != nil {
		log.Fatal(err3)
	}
	fmt.Println("周长:", ret)
}

2.6 jsonrpc - 实现 - 支持tcp,支持跨语言,但是不支持http

因为rpc只能用于go开发的服务之间进行通信,所以下面采用jsonrpc实现跨语言实现rpc

Example:

server.go

package main

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

type Params struct {
	Width, Height int
}
type Rect struct {
}

func (r *Rect) Area(p Params, ret *int) error {
	*ret = p.Width * p.Height
	return nil
}
func (r *Rect) Perimeter(p Params, ret *int) error {
	*ret = (p.Height + p.Width) * 2
	return nil
}
func main() {
	rpc.Register(new(Rect))
	lis, err := net.Listen("tcp", ":8096")
	if err != nil {
		log.Panicln(err)
	}
	for {
		conn, err := lis.Accept()
		if err != nil {
			continue
		}
		go func(conn net.Conn) {
			fmt.Println("new client")
			jsonrpc.ServeConn(conn)
		}(conn)
	}
}

client.go

package main

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

type Param struct {
	Width, Height int
}

func main() {
	conn, err := jsonrpc.Dial("tcp", ":8096")
	if err != nil {
		log.Panicln(err)
	}
	ret := 0
	err2 := conn.Call("Rect.Area", Param{50, 100}, &ret)
	if err2 != nil {
		log.Panicln(err2)
	}
	fmt.Println("面积:", ret)
	err3 := conn.Call("Rect.Perimeter", Param{50, 100}, &ret)
	if err3 != nil {
		log.Panicln(err3)
	}
	fmt.Println("周长:", ret)
}

php客户端调用go server服务

<?php

class Client {

    private $conn;

    function __construct($host, $port) {
        $this->conn = fsockopen($host, $port, $errno, $php_errormsg, 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 false;
        }

        return json_decode($line, true);
    }
}

$client = new Client("127.0.0.1", "8096");
$args = array("A" => 9, "B" => 2);
$res = $client->Call("Arith.Multiply", $args);
printf("%d * %d = %d\n", $args["A"], $args["B"], $res["result"]["Pro"]);

$r = $client->Call("Arith.Divide", $args);
printf("%d / %d, Quo is %d, Rem is %d\n", $args['A'], $args['B'], $r['result']['Quo'], $r['result']['Rem']);

2.7 protorpc - 实现 - 支持tcp和http, 支持跨语言

安装:下载地址mac64位

下载完,解压。(个人习惯将文件重新命名为protoc-3.5.0-osx,移动到用户跟目录下/Users/cc)

// 配置全局变量
vim ~/.zshrc

#protoc
export PATH=/Users/cc/protoc-3.5.0-osx/bin:$PATH

source ~/.zshrc

protoc --version //查看

切换到自己的项目目录下,在go中安装protobuf相关的库

go get -u github.com/golang/protobuf/protoc-gen-go

在这里插入图片描述

Note : 执行安装包错

  1. 现在想要拉取protoc-gen-go需要去google.golang.org/protobuf拉取,原来的路径已经废弃了。
  2. 我使用的go版本是1.17。而Go1.17版使用go install安装依赖。所以应该按照它下面的格式go install pkg@version进行拉取。

把上面的命令改为:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

安装成功之后,创建一个.proto文件

pb/Arith.proto

syntax = "proto3";
package db;

// 算术运算符结构
message ArithRequest {
  int32 a = 1;
  int32 b = 2;
}

// 算术运算符响应结构
message ArithReponse {
  // 乘积
  int32 pro = 1;
  // 商
  int32 que = 2;
  // 余数
  int32 rem = 3;
}

// rpc方法
service ArithService {
  // 乘法运算方法
  rpc multiply (ArithRequest) returns (ArithReponse);
  // 除法运算方法
  rpc divide (ArithRequest) returns (ArithReponse);
}

执行protoc --go_out=./pb ./pb/arith.proto

发现又报错,说是环境变量的问题

在这里插入图片描述

解决:vim ~/.zshrc (我配的zsh,自行修改自己的),修改完执行 source ~/.zshrc命令,重启iterm2或者idea即可

export GOBIN=/Users/cc/go/bin
export PATH=$GOBIN:$PATH

在这里插入图片描述

再次执行protoc --go_out=./pb ./pb/arith.proto , 在pb目录下生成了arith.pb.go文件

基于生成的arith.pb.go代码我们来实现一个rpc服务端

标签:服务,nil,err,rpc,ret,go,RPC,Go,conn
来源: https://blog.csdn.net/weixin_43713498/article/details/123612006

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有