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

go语言 RPC
0 71
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

回帖
  • 消灭零回复
广告位招租 100元/月 广告位招租 100元/月
相关主题
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
相关主题
原来EUC-CN是gb2312编码的一种表示方法 0
索鸟快传2.1.0版本发布 0
golang网络数据读取timeout的处理 – 以SetReadDeadline为例 0
索鸟网广告位招租 0
golang利用通道chan实现一个通用的TCP连接池 0
frpc和frps的交互流程详解了解了frp是如何进行TCP代理的 0
golang实现内存池 go语言字节池byte pool实现代码 0
golang网络编程之基于TCP协议实现长连接 golang心跳检测 0
国产光刻机产业链最新核心材料ArF 193nm光刻胶通过用户认证 0
javascript数组相关函数length属性、delete关键字、pop()栈方法、shift() 0
Qt为我们提供了几个可以用于线程Sleep的函数msleep和usleep 0
QT程序闪退错误捕获教程利用DbgHelp 错误调试技术 0
简单快速修改Qtcreator项目工程的名称 0
golang 的interface接口类型断言 0
golang类型断言type的使用 0
golang依赖包管理 mod使用教程 0
golang表单验证库validator 0
如何使用Go语言实现一个简单的异步任务框架呢?生产者消费者模型 0
内网穿透反向代理工具frp实现TCP协议代理源码分析 0
内网穿透反向代理库frp的实现原理分析一 0