• 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

  • 相关阅读:
    Windows下利用TortoiseSVN搭建本地SVN服务器
    我的Netty笔记
    Netty简单的HTTP服务器
    开启和关闭HBase的thrift进程
    java中重载和重写的区别
    java中形参个数可变的方法使用
    java中方法的参数传递机制
    Java内存分配全面浅析
    java中的类修饰符、成员变量修饰符、方法修饰符。
    js实现页面重定向
  • 原文地址:https://www.cnblogs.com/tsxylhs/p/13592364.html
Copyright © 2020-2023  润新知