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

0 430
索鸟 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

回帖
  • 消灭零回复
相关主题
2020年最新最新Kubernetes视频教程(K8s)教程 2
程序员转型之制作网课变现,月入过万告别996 1
索鸟快传2.0发布啦 1
两个不同网络的电脑怎么实现文件的互相访问呢? 1
网盘多账号登录软件 1
Java实战闲云旅游项目基于vue+element-ui 1
单点登录技术解决方案基于OAuth2.0的网关鉴权RSA算法生成令牌 1
QT5获取剪贴板上文本信息QT设置剪贴板内容 1
springboot2实战在线购物系统电商系统 1
python web实战之爱家租房项目 1
windows COM实用入门教程 1
C++游戏开发之C++实现的水果忍者游戏 1
计算机视觉库opencv教程 1
node.js实战图书管理系统express框架实现 1
C++实战教程之远程桌面远程控制实战 1
相关主题
PHP7报A non well formed numeric value encountered 0
Linux系统下关闭mongodb的几种命令分享 0
mongodb删除数据、删除集合、删除数据库的命令 0
Git&Github极速入门与攻坚实战课程 0
python爬虫教程使用Django和scrapy实现 0
libnetsnmpmibs.so.31: cannot open shared object file 0
数据结构和算法视频教程 0
redis的hash结构怎么删除数据呢? 0
C++和LUA解析器的数据交互实战视频 0
mongodb errmsg" : "too many users are authenticated 0
C++基础入门视频教程 0
用30个小时精通C++视频教程可能吗? 0
C++分布式多线程游戏服务器开发视频教程socket tcp boost库 0
C++培训教程就业班教程 0
layui的util工具格式时间戳为字符串 0
C++实战教程之远程桌面远程控制实战 1
网络安全培训视频教程 0
LINUX_C++软件工程师视频教程高级项目实战 0
C++高级数据结构与算法视频教程 0
跨域问题很头疼?通过配置nginx轻松解决ajax跨域问题 0