Go语言学习笔记(十二)Tcp实现Rpc

1.Tcp实现Rpc:

既然可以直接利用Tcp传递消息,相对于Http来说便不在需要相关处理..同时利用Http实现Rpc时,服务端可以自动接管网络连接.然后负责对象连接管理.Tcp只能自行处理.

1.1服务端:

go 复制代码
package main

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

func main() {
	rpc.Register(new(UserHandler))
	listen, err := net.Listen("tcp", "127.0.0.1:8090")

	if err != nil {
		fmt.Println("监听tcp端口出错")
		return
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("建立tcp连接失败")
			continue
		}

		go func(conn net.Conn) {
			rpc.ServeConn(conn)
		}(conn)
	}
}

type UserHandler struct {
}

type Userdetail struct {
	Id       int
	Username string
	Nickname string
}

// 获取用户信息的方法绑定到结构体
func (handler *UserHandler) GetUserInfo(name string, user *Userdetail) (err error) {
	fmt.Println("try to get user info by %s", name)

	//校验入参.
	if len(name) == 0 {
		return errors.New("the user name is empty")
	}

	user.Id = 001
	user.Username = "itbo"
	user.Nickname = "尊敬的itbo"

	return nil
}

1.2客户端:

go 复制代码
package main

import (
	"fmt"
	"net/rpc"
)

func main() {
	//利用Tcp连接远程服务端.
	client, err := rpc.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
		fmt.Println("与服务端建立连接出错")
		return
	}

	defer client.Close()

	user := new(Userdetail)
	client.Call("UserHandler.GetUserInfo", "golang", user)

	if err != nil {
		fmt.Println("获取用户信息出错", err)
		return
	}
	//打印获取的信息
	fmt.Printf("用户id:%+v\n", user.Id)
	fmt.Printf("用户名:%+v\n", user.Username)
	fmt.Printf("用户昵称:%+v\n", user.Nickname)

}

1.3执行结果:

1.4Http和Tcp实现Rpc区别:

Tcp实现的服务端代码失去了rpc.HandleHttp和http.Serve这两个函数的调用.其中rpc.HandleHttp用于注册默认的url和处理函数的映射关系.

打开http.service源码可以看到.

通过这里接收连接然后去处理.因为没有调用这个函数.所以tcp需要自己去处理连接.

2.Json格式实现Rpc通信:

2.1服务端:

go 复制代码
package main

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

func main() {
	rpc.Register(new(UserHandler))
	listen, err := net.Listen("tcp", "127.0.0.1:8090")

	http.Serve(listen, nil)
	if err != nil {
		fmt.Println("监听tcp端口出错")
		return
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("建立tcp连接失败")
			continue
		}

		go func(conn net.Conn) {
			//利用jsonRpc处理连接.
			jsonrpc.ServeConn(conn)
		}(conn)
	}
}

type UserHandler struct {
}

type Userdetail struct {
	Id       int
	Username string
	Nickname string
}

// 获取用户信息的方法绑定到结构体
func (handler *UserHandler) GetUserInfo(name string, user *Userdetail) (err error) {
	fmt.Println("try to get user info by %s", name)

	//校验入参.
	if len(name) == 0 {
		return errors.New("the user name is empty")
	}

	user.Id = 001
	user.Username = "itbo"
	user.Nickname = "尊敬的itbo"

	return nil
}

2.2jsonrpc.ServeConn源码:

scss 复制代码
func ServeConn(conn io.ReadWriteCloser) {
	rpc.ServeCodec(NewServerCodec(conn))
}

2.3NewServerCodec源码:

可以看到底层有一个编码器和解码器.

go 复制代码
func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
	return &serverCodec{
		dec:     json.NewDecoder(conn),
		enc:     json.NewEncoder(conn),
		c:       conn,
		pending: make(map[uint64]*json.RawMessage),
	}
}

2.4客户端:

go 复制代码
package main

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

func main() {
	//利用Tcp连接远程服务端.
	//client, err := rpc.Dial("tcp", "127.0.0.1:8090")
	client, err := jsonrpc.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
		fmt.Println("与服务端建立连接出错")
		return
	}

	defer client.Close()

	user := new(Userdetail)
	client.Call("UserHandler.GetUserInfo", "golang", user)

	if err != nil {
		fmt.Println("获取用户信息出错", err)
		return
	}
	//打印获取的信息
	fmt.Printf("用户id:%+v\n", user.Id)
	fmt.Printf("用户名:%+v\n", user.Username)
	fmt.Printf("用户昵称:%+v\n", user.Nickname)
}
相关推荐
糖拌西瓜皮2 小时前
Java开发者视角:深入理解Node.js异步编程模型
java·后端·node.js
雪隐2 小时前
个人电脑玩AI-04让5060 Ti给你打工——本地claude code编程助理
人工智能·后端
AskHarries2 小时前
Browser Tool:网页打开、点击、输入、截图和验证
后端
程序员cxuan2 小时前
分享一下我最近常用的 10 个 Codex 小技巧。
人工智能·后端·程序员
一线大码2 小时前
Smart-Doc 的简单使用
java·后端·restful
喵个咪2 小时前
技术复盘:基于 go-wind-cms 的官网+商城双业务渐进拆分实战
后端·架构·go
ZengLiangYi3 小时前
批量导入 1000 条对话的性能优化实战
javascript·后端·架构
juejin9983 小时前
Claude Code 环境跑通:第一次有效对话
后端
wei_shuo3 小时前
KES 数据库迁移实战:从 Oracle/MySQL 到 KingbaseES 的平滑过渡指南
后端