返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

rpc

发布于 2024-10-12 19:15:53 字数 3374 浏览 0 评论 0 收藏 0

远程调用 (Remote Procedure Call)是种通信协议,允许像调用本地代码那样去调用另一台主机的服务。标准库实现了基于 TCP、HTTP 协议的远程调用。


服务限制:

  • 必须是导出类型。
  • 必须是导出方法。
  • 参数:
  • 第一参数为调用参数。(通常使用复合类型指针)
  • 第二参数是返回值指针。
  • 必须是导出类型。
  • 可被序列化,默认 encoding/gob
  • 返回值必须是 error

TCP

标准库提供了默认服务器( DefaultServer )和便捷函数。
接入方法 Accept 为每个客户端建立一个 goroutine 服务。

package main

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

type Service struct{}

func (*Service) Add(args []int, reply *int) error {
	*reply = args[0] + args[1]
	return nil
}

// -----------------------------------------------

func server() {
	l, err := net.Listen("tcp", ":8181")  // !close
	if err != nil { log.Fatalln(err) }

	srv := rpc.NewServer()
	srv.RegisterName("my", new(Service))
    
	go srv.Accept(l)
}

func test() {
	client, err := rpc.Dial("tcp", "127.0.0.1:8181")
	if err != nil { log.Fatalln(err) }
	defer client.Close()

	var x int
	if err := client.Call("my.Add", []int{2, 3}, &x); err != nil {
		log.Fatalln(err)
	}

	fmt.Println(x)
}

func main() {
	log.SetFlags(log.Lshortfile)

	server()
    // time.Sleep(time.Second)
	test()
}

客户端可使用异步模式。

func test() {
	client, err := rpc.Dial("tcp", "127.0.0.1:8181")
	if err != nil { log.Fatalln(err) }
	defer client.Close()

	var x int
	call := client.Go("my.Add", []int{2, 3}, &x, nil)
	
	// call == reply
	if reply := <- call.Done; reply.Error != nil {
		log.Fatalln(err)
	}

	fmt.Println(x)
}

改用其他编码。

package main

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

type Service struct{}

func (*Service) Add(args []int, reply *int) error {
	*reply = args[0] + args[1]
	return nil
}

// -----------------------------------------------

func server() {
	l, err := net.Listen("tcp", ":8181")
	if err != nil { log.Fatalln(err) }

	srv := rpc.NewServer()
	srv.RegisterName("my", new(Service))

	go func() {
		for {
			conn, err := l.Accept()
			if err != nil { log.Fatalln(err)}
			go srv.ServeCodec(jsonrpc.NewServerCodec(conn)) // !!!
		}
	}()
}

func test() {
	client, err := jsonrpc.Dial("tcp", "127.0.0.1:8181") // !!!
	if err != nil { log.Fatalln(err) }
	defer client.Close()

	var x int
	if err := client.Call("my.Add", []int{2, 3}, &x); err != nil {
		log.Fatalln(err)
	}

	fmt.Println(x)
}

func main() {
	log.SetFlags(log.Lshortfile)

	server()
    // time.Sleep(time.Second)
	test()
}

HTTP

除协议不同外,其他逻辑基本一致。

package main

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

type Service struct{}

func (*Service) Add(args []int, reply *int) error {
	*reply = args[0] + args[1]
	return nil
}

// -----------------------------------------------

func server() {
	srv := rpc.NewServer()
	srv.RegisterName("my", new(Service))

	http.Handle("/rpc", srv)
	go http.ListenAndServe(":8181", nil)
}

func test() {
	client, err := rpc.DialHTTPPath("tcp", "127.0.0.1:8181", "/rpc")
	if err != nil { log.Fatalln(err) }
	defer client.Close()

	var x int
	if err := client.Call("my.Add", []int{2, 3}, &x); err != nil {
		log.Fatalln(err)
	}

	fmt.Println(x)
}

func main() {
	log.SetFlags(log.Lshortfile)

	server()
    time.Sleep(time.Second) // wait server !!!
	test()
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文