golang 实现rpc远程调用,开箱即用

RPC 远程方法调用

优点:
提升系统可扩展性,
提升可维护性,和吃持续交付能力
实现系统的高可用等

缺点

rpc受限于网络


实现一个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

posted @ 2020-08-31 22:28  菜鸟的进击  阅读(906)  评论(0编辑  收藏  举报