自己动手利用go语言实现一个简易版RPC服务器

go语言 RPC
0 336
suoniao 2021-01-20
需要:0索币

实现一个rcp远程调用关键在于带里层的实现

还是贴代码吧

package client

import (
    "bytes"
    "fmt"
    "github.com/gorilla/rpc/json"
    "net/http"
    "time"
)

//声明clent 链接客户端地址
type Client struct {
    Address string
}

//将client 地址赋值
func New(addr string) *Client {
    return &Client{
        Address: addr,
    }
}

//jrp实现
func (c *Client) jrpc(method string, in interface{}, out interface{}) error {
    message, err := json.EncodeClientRequest(method, in)
    if err != nil {
        return err
    }
    //封装Http请求作物rpc 的载体
    fmt.Println("c.address", c.Address)
    req, err := http.NewRequest(http.MethodPost, c.Address, bytes.NewBuffer(message))

    req.Header.Set("Content-Type", "application/json")
    client := &http.Client{
        Timeout: 10 * time.Second,
    }
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("请求执行失败")
        return err
    }
    defer resp.Body.Close()
    return json.DecodeClientResponse(resp.Body, out)
}

type PingMessage struct {
    Payload string
}

//测试远程服务是否启动
func (c *Client) Ping(message string) (string, error) {
    in := PingMessage{Payload: message}
    var out PingMessage
    err := c.jrpc("TEST.Ping", in, &out)
    if err != nil {
        fmt.Println("接口调用失败", err)
        return "", err
    }
    return out.Payload, nil
}

//其他方法加入开箱加入即可(代理层代码)

A调用B

A层实现

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "jrpc_test/client"
    "log"
)

func main() {
    router := gin.Default()
    //router := gin.New()

    router.GET("/v1/registry/sign", func(context *gin.Context) {
        log.Println(">>>> hello jrpc <<<<")
        _decoderClient := client.New("http://127.0.0.1:9999/rpc")
        resq, err := _decoderClient.Ping("我的接口通了")
        if err != nil {
            fmt.Println("接口调用失败")
        } else {
            fmt.Println("远程调用成功", resq)
        }
        context.JSON(200, gin.H{
            "code":    200,
            "success": true,
        })
    })
    // 指定地址和端口号
    router.Run("localhost:8888")
}

B层代码实现

package main

import (
    "context"
    "flag"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
    "jrpc_test/service"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"
)

var bind = flag.String("bind", ":9999", "server port")
var port = 9999

func main() {
    s := rpc.NewServer()
    //可传递参数配置文件参数等
    w, _ := service.W.New()
    s.RegisterCodec(json.NewCodec(), "application/json")
    err := s.RegisterService(&service.ControlService{Work: w}, "TEST")
    if err != nil {
        panic(err)
    }
    http.Handle("/rpc", s)

    srv := &http.Server{
        Addr: ":9999",
    }
    //保持心跳,
    service.W.Sign(port)
    go func() {
        //监听注册rpc服务
        if err = srv.ListenAndServe(); err != nil {
            log.Fatal(err)
        }
    }()
    //make channel 终止程序
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit
    log.Println("程序终止")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatalf("服务终止: %v", err)
    }

}

B层方法实现

package service

import (
    "bytes"
    "database/sql"
    "encoding/json"
    "fmt"
    "jrpc_test/client"
    "net/http"
    "sync"
    "time"
)

type Work struct {
    sync.Mutex
    db *sql.DB
//可多个handler } type ( // SignPayload represent a request for vip-processor SignPayload struct { Name string `json:"server_name"` Address string `json:"server_addr"` Port int `json:"server_port"` } ) var W Work //work可实现具体任务 func (w *Work) AddTask() error { fmt.Println("远程调用此方法") return nil } //在new 里初始化一些中间建如db等 func (w *Work) New() (*Work, error) { worker := &Work{} return worker, nil } type ControlService struct { Work *Work } func (c *ControlService) Ping(r *http.Request, in *client.PingMessage, out *client.PingMessage) error { out.Payload = in.Payload fmt.Println("我是远程服务我已启动,谢谢") return nil } func (c *Work) Sign(port int) { //获取心跳地址 var addr string = "127.0.0.1:8888" go func(_addr string) { c := http.Client{ Timeout: 250 * time.Millisecond, } for { _payload := &SignPayload{ Name: "sign", Address: "127.0.0.1", Port: port, } _reqURL := "http://" + _addr + "/v1/registry/sign" blob, err := json.Marshal(_payload) if err != nil { return } req, err := http.NewRequest(http.MethodGet, _reqURL, bytes.NewBuffer(blob)) if err != nil { fmt.Println("error", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Connection", "close") resp, err := c.Do(req) if err != nil { fmt.Println("心跳链接中断", err) } if resp != nil { if err := resp.Body.Close(); err != nil { panic("Close response body error") } } time.Sleep(time.Duration(3 * time.Second)) } }(addr) }

至此简单的rpc服务就启动了,但是要想实现复杂的逻辑需补充方法,结构体中需要添加其必要的初始化信息

 github地址:https://github.com/tsxylhs/golang_jrpc_test.git

回帖
  • 消灭零回复
相关主题
golang网络数据读取timeout的处理 – 以SetReadDeadline为例 0
golang实现http中间件 gin框架中间件实现原理分析 0
自己动手利用go语言实现一个简易版RPC服务器 0
Go语言数组定义和初始化的三种方式 0
通过go env命令查询go语言环境变量信息 0
GOPATH目录下面创建bin目录、pkg目录和src目录功能说明 0
windows配置golang的环境变量 gopath配置学习go语言第一天 0
何用Go语言开发一个高负荷的WebSocket服务 0
go语言获取windows磁盘列表的代码golang获取磁盘使用信息容量剩余量 0
Go语言实战文本大数据挖掘 0
go语言开发博客系统实战 0
Go语言从基础入门到商品秒杀系统实战18天培训课程 0
Netty分布式RPC实战Netty源码剖析Netty5各种RPC架构实战 0
用go语言替代python实现自动化运维实战 0
用go语言实现一个高并发的抽奖系统 0
golang从基础入门到分布式爬虫项目实战 0
rocketMQ远程下单RPC接口设计与实现 0
go语言与区块链精品入门课程视频 0
go商品秒杀后台教程golang聊天室redis实现 0
go语言入门视频教程 0
相关主题
打印机USB驱动开发之实现打印服务器 0
Qt利用QLabel组件来显示图片 0
TableView自定义代理QStyledItemDelegate实现ComboBox 0
Qt利用QGraphicsView类实现图片放大缩小平移显示 0
Qt实现非阻塞延迟方法sleep 0
海康相机SDK的C++对应的接口 0
Qt实现webdav客户端功能支持https协议的webdav客户端 0
CHKDSK解决 移动硬盘只能看见盘符其它信息都看不见另外双击也打不开 0
gogs一直报errror:dial tcp xxx.xxx.xxx.xxx 宿主机的ip 0
索鸟快传2.1.2发布 0
索鸟快传2.1.1发布 0
Qt操作windows注册表的方法 bat从注册表中将键值删除 0
重写QSqlQueryModel实现QTableView显示图片 0
QLocalServer基于本地套接字socket的服务端server 0
Qt使用动态库的方法 QLibrary库的典型用法 0
QT实现视频播放器界面开发 0
QTableview实现鼠标放上面显示不同颜色 0
Qt的QTableView自定义委托详解 0
QTableView利用自定义委托实现日期显示下拉菜单文字颜色等 0
Qt利用QApplication::sendEvent和QMouseEvent模拟鼠标点击事件 0