前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微服务系列笔记之RPC和WebSocket

微服务系列笔记之RPC和WebSocket

作者头像
陌无崖
发布2019-08-16 17:56:32
2.8K0
发布2019-08-16 17:56:32
举报

不忘初心,砥砺前行

作者 | 陌无崖

转载请联系授权

导语

这一篇文章会陆续介绍Micro API中的rpc模式和websocket模式,废话不多说,阅读前要保持头脑清晰就可以了。

RPC模式

首先同样定义我们的api.proto和之前的代码一样

syntax = "proto3";

service Example {
    rpc Call(CallRequest) returns(CallResponse) {};
}

service Foo {
    rpc Bar(EmptyRequest) returns(EmptyResponse) {};
}

message CallRequest {
    string name = 1;
}

message CallResponse {
    string message = 2;
}

message EmptyRequest {
}

message EmptyResponse {
}

执行protoc命令,生成我们的代码

protoc  --go_out=. --micro_out=. proto/api.proto

来看我们的服务端代码,实现我们的服务方法

type Example struct{}

type Foo struct{}

// Call 方法会接收由API层转发,路由为/example/call的HTTP请求
func (e *Example) Call(ctx context.Context, req *proto.CallRequest, rsp *proto.CallResponse) error {
    log.Print("收到 Example.Call 请求")

    if len(req.Name) == 0 {
        return errors.BadRequest("go.micro.api.example", "no content")
    }

    rsp.Message = "RPC Call收到了你的请求 " + req.Name
    return nil
}

// Bar 方法会接收由API层转发,路由为/example/foo/bar的HTTP请求
// 该接口我们什么参数也不处理,只打印信息
func (f *Foo) Bar(ctx context.Context, req *proto.EmptyRequest, rsp *proto.EmptyResponse) error {
    log.Print("收到 Foo.Bar 请求")
    return nil
}

编写我们的主函数

func main() {
    service := micro.NewService(
        micro.Name("go.micro.api.example"),
    )

    service.Init()

    // 注册 example 接口
    proto.RegisterExampleHandler(service.Server(), new(Example))

    // 注册 foo 接口
    proto.RegisterFooHandler(service.Server(), new(Foo))

    if err := service.Run(); err != nil {
        log.Fatal(err)
    }
}

现在测试我们的代码 以rpc模式运行API

micro api --handler=rpc

运行服务端代码

go run rpc.go

使用postman进行测试

WebSocket模式

Websocket时一种双向通信的套接字,可以主动向服务端发送请求,并完成响应,这里不再进行详细介绍,如果有不懂的欢迎在我的知识星球进行讨论。加入方式如下

首先编写一个客户端的index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Websocket Stream</title>
    <script src="./main.js"></script>
    <style>
        table {
            table-layout: fixed;
        }
        td {
            border: 2px solid green;
        }
        td input {
            width: 100%;
            box-sizing: border-box;
        }
    </style>
</head>
<body>
<h2>Websocket Stream</h2>
<table>
    <tr>
        <td valign="top" width="25%">
            <p>
                <form>
            <p> Name: <br>
                <input id="name" name="name" type="text"/>
            </p>
            <p>
                <button type="button" id="send">Send</button>
                <button type="button" id="cancel">Cancel</button>
            </p>
            <p>
                <button type="button" id="open">Open Connection</button>
            </p>
            </form>
            </p>
        </td>
        <td valign="top" width="75%">
            <div id="output"/>
        </td>
    </tr>
</table>
</body>
</html>

以上需要注意的时引用了main.js,在第六行,代码如下,这里需要注意的是,在第2行定义了一个变量,这个变量存储了我们连接socket的地址,然后使用new WebSocket(wsUri)建立了一个websocket对象,这个对象监听了4个事件,分别是

onopen:监听与服务端连接成功执行代码 onclose:监听与服务端断开时执行相应代码 onmessage:监听收到服务端信息时执行代码 onerror:监听与服务端通信期间出现了错误,执行相应代码

window.addEventListener("load", function (evt) {
    var wsUri = "ws://localhost:8080/websocket"
    var output = document.getElementById("output");
    var nameTxt = document.getElementById("name");
    var ws;

    var print = function (message) {
        var d = document.createElement("div");
        d.innerHTML = message;
        output.appendChild(d);
    };

    (function () {
        ws = new WebSocket(wsUri);

        ws.onopen = function (evt) {
            print('<span style="color: green;">Connection Open</span>');
        }
        ws.onclose = function (evt) {
            print('<span style="color: red;">Connection Closed</span>');
            ws = null;
        }
        ws.onmessage = function (evt) {
            print('<span style="color: blue;">Update: </span>' + evt.data);
        }
        ws.onerror = function (evt) {
            print('<span style="color: red;">Error: </span>' + evt.data);
        }
    })();


    document.getElementById("send").onclick = function (evt) {
        if (!ws) {
            return false
        }

        var msg = {hi: nameTxt.value}

        req = JSON.stringify(msg)
        print('<span style="color: blue;">Sent request: </span>' + req);
        ws.send(JSON.stringify(msg));

        return false;
    };

    document.getElementById("cancel").onclick = function (evt) {
        if (!ws) {
            return false;
        }
        ws.close();
        print('<span style="color: red;">Request Canceled</span>');
        return false;
    };

    document.getElementById("open").onclick = function (evt) {
        if (!ws) {
            newSocket()
        }
        return false;
    };
})

客户端写好了,我们就可以编写我们的服务端,首先需要导入相应的包

"github.com/gorilla/websocket"
"github.com/micro/go-micro/web"

服务端对socket进行升级时,这里为了方便直接验证通过

var upGrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

编写一个读取客户端消息的函数,这里需要明白的upGrader.Upgrade(w, r, nil)是升级HTTP并连接到websocket上获得一个websocket连接。中间使用了一个无限循环进行读取消息和写入消息。

func hi(w http.ResponseWriter, r *http.Request) {

    c, err := upGrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("upgrade: %s", err)
        return
    }

    defer c.Close()
    for {
        mt, message, err := c.ReadMessage()
        if err != nil {
            log.Println("read:", err)
            break
        }

        log.Printf("recv: %s", message)

        err = c.WriteMessage(mt, message)
        if err != nil {
            log.Println("write:", err)
            break
        }
    }
}

在主函数中注册我们的服务

service := web.NewService(
    web.Name("go.micro.web.websocket"),
)
    if err := service.Init(); err != nil {
    log.Fatal("Init", err)
}
// static files
service.Handle("/websocket/", http.StripPrefix("/websocket/", http.FileServer(http.Dir("html"))))
// websocket interface
service.HandleFunc("/websocket", hi)
if err := service.Run(); err != nil {
log.Fatal("Run: ", err)
}

现在我们运行测试一下

micro api --handler=web --namespace=go.micro.web
go run .\web.go

打开浏览器输入

END

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang技术杂文 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
  • RPC模式
  • WebSocket模式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档