go自带rpc框架生产环境使用demo

基础使用

序列化使用自带gob协议

server

复制代码
package main

import (
	"net"
	"net/rpc"
)

// 定义一个handler结构体
type HelloService struct {
}

// 定义handler方法,大小写,参数,返回值都是固定的,否则无法注册
func (receiver *HelloService) Hello(req string, rep *string) error {
	*rep = "hello " + req
	return nil
}

func main() {
//1. 实例化一个server
	listen, _ := net.Listen("tcp", ":8001")
	//2. 注册handler
	_ = rpc.RegisterName("HelloService", &HelloService{})
	// 3. 启动服务

	for {
		conn, _ := listen.Accept()
		go rpc.ServeConn(conn) //避免阻塞
	}

}

}

client

复制代码
package main

import (
	"fmt"
	"net/rpc"
)

func main() {
	//1. 建立连接
	client, _ := rpc.Dial("tcp", "localhost:8001")
	var data *string = new(string)
	err := client.Call("HelloService.Hello", "matthew", data)
	if err != nil {
		fmt.Println("调用失败")
	}
	fmt.Println("success: ", *data)
}

注:两个文件需要在不同包下面

使用json序列化

server

复制代码
package main

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

// 定义一个handler结构体
type HelloService struct {
}

// 定义handler方法,大小写,参数,返回值都是固定的,否则无法注册
func (receiver *HelloService) Hello(req string, rep *string) error {
	*rep = "hello " + req
	return nil
}

func main() {
	jsonserver()
}

/*
*
go 默认的序列化反序列化协议是gob
*/
func gobserver() {
	//1. 实例化一个server
	listen, _ := net.Listen("tcp", ":8001")
	//2. 注册handler
	_ = rpc.RegisterName("HelloService", &HelloService{})
	// 3. 启动服务

	for {
		conn, _ := listen.Accept()
		go rpc.ServeConn(conn) //避免阻塞
	}
}

/*
*
使用json来序列化和反序列化
*/
func jsonserver() {
	//1. 实例化一个server
	listen, _ := net.Listen("tcp", ":8001")
	//2. 注册handler
	_ = rpc.RegisterName("HelloService", &HelloService{})
	// 3. 启动服务

	for {
		conn, _ := listen.Accept()
		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) //避免阻塞
	}

}

client

复制代码
package main

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

func main() {
	jsonRpcClient()
}

func jsonRpcClient() {
	//1. 建立连接
	client, _ := net.Dial("tcp", "localhost:8001")
	var data *string = new(string)
	codeclient := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(client))
	err := codeclient.Call("HelloService.Hello", "matthew", data)
	if err != nil {
		fmt.Println("调用失败")
	}
	fmt.Println("success: ", *data)
}

构建一个规范的rpc项目框架

如果想构建生产环境使用的rpc服务,不但要能实现业务,还要有良好的架构设计

  • client 客户端
    • client_proxy/stub 客户端代理封装
    • handler 句柄封装
  • server 服务端
    • server_proxy/stub 服务端函数封装
    • handler 句柄封装

句柄相当于服务端和客户端互相通信的通道,告诉双方可以提供的方法

client.go

复制代码
package main

import (
	"fmt"
	"learngo/chw01/nicerpc/client_stub_proxy"
)

func main() {
	stub := client_stub_proxy.NewHelloServiceStub("tcp", "localhost:9001")
	res := new(string)
	stub.Hello("matthew", res)
	fmt.Println(*res)
}

client_stub_proxy

复制代码
package client_stub_proxy

import (
	"fmt"
	"learngo/chw01/nicerpc/handler"
	"net/rpc"
)

/**
存放客户端proxy或者stub,封装已经注册的服务和方法列表,用于客户端快速调用专注业务
*/

type HelloServiceStub struct {
	*rpc.Client
}

// 构造函数返回一个,服务代理并携带客户端
func NewHelloServiceStub(protcol, address string) HelloServiceStub {
	client, err := rpc.Dial(protcol, address)
	if err != nil {
		fmt.Println(err)
		panic("rpc client start panic")
	}
	return HelloServiceStub{client}
}

/*
*
封装了HelloService服务的Hello方法
*/
func (cs *HelloServiceStub) Hello(req string, reply *string) error {
	err := cs.Call(handler.HelloServiceName+".Hello", req, reply)
	return err
}

hander.go

复制代码
package handler

/*
*
定义所有的handler名称,可以被client,server两端引入对齐
*/
const HelloServiceName = "hander/HelloService"

// 定义一个handler结构体
type HelloService struct {
}

// 定义handler方法,大小写,参数,返回值都是固定的,否则无法注册
func (receiver *HelloService) Hello(req string, rep *string) error {
	*rep = "hello " + req
	return nil
}

server.go

复制代码
package main

import (
	"fmt"
	"learngo/chw01/nicerpc/handler"
	"learngo/chw01/nicerpc/server_proxy_stub"
	"net"
	"net/rpc"
)

func main() {
	var address string = ":9001"
	//实例化一个server
	listen, err := net.Listen("tcp", address)
	if err != nil {
		panic("server start error " + address)
	}
	fmt.Println("server start success on address: ", address)
	//注册方法
	server_proxy_stub.RegisterHelloServicer(&handler.HelloService{})
	//启动服务
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("connect error", err)
		}
		rpc.ServeConn(conn)
	}
}

server_proxy_stub

复制代码
package server_proxy_stub

import (
	"learngo/chw01/nicerpc/handler"
	"net/rpc"
)

/**
封装所有server的方法(业务逻辑)
*/

/*
*
多态:顶一个HelloServie的接口,凡是实现了该接口方法的struct都继承了接口
*/
type HelloServicer interface {
	Hello(req string, reply *string) error
}

// 当前的方法只能用来HelloService一种结构体,我们关注的不是结构体而是结构体的方法
func RegisterHelloService(srv *handler.HelloService) error {
	return rpc.RegisterName(handler.HelloServiceName, srv)
}

// 对比上面的注册,通过接口来注册更加灵活。所有实现了HelloServicer接口的struct都可以直接使用该方法
func RegisterHelloServicer(srv *handler.HelloService) error {
	return rpc.RegisterName(handler.HelloServiceName, srv)
}
相关推荐
QT 小鲜肉8 分钟前
【QT随笔】结合应用案例一文完美概括QT中的队列(Queue)
c++·笔记·qt·学习方法·ai编程
OxYGC44 分钟前
[玩转GoLang] 5分钟整合Gin / Gorm框架入门
开发语言·golang·gin
却尘1 小时前
Server Actions 深度剖析:这就是个披着 React 外衣的 RPC
前端·rpc·next.js
向上的车轮3 小时前
基于go语言的云原生TodoList Demo 项目,验证云原生核心特性
开发语言·云原生·golang
SNAKEpc121383 小时前
QML和Qt Quick
c++·qt
PH_modest3 小时前
【Qt跬步积累】—— 初识Qt
开发语言·qt
360智汇云4 小时前
k8s交互桥梁:走进Client-Go
golang·kubernetes·交互
qq_172805595 小时前
Go 装饰器模式学习文档
学习·golang·装饰器模式
刘梓谦6 小时前
如何在Qt中使用周立功USB转CAN卡
开发语言·qt·zlg·周立功
江公望6 小时前
Qt QML实现无边框窗口
开发语言·qt