转载

微服务系列笔记之RPC和WebSocket

不忘初心,砥砺前行  

微服务系列笔记之RPC和WebSocket

作者 | 陌无崖

转载请联系授权 

导语

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

RPC模式

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

 1syntax = "proto3";
 2
 3service Example {
 4    rpc Call(CallRequest) returns(CallResponse) {};
 5}
 6
 7service Foo {
 8    rpc Bar(EmptyRequest) returns(EmptyResponse) {};
 9}
10
11message CallRequest {
12    string name = 1;
13}
14
15message CallResponse {
16    string message = 2;
17}
18
19message EmptyRequest {
20}
21
22message EmptyResponse {
23}

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

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

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

 1type Example struct{}
 2
 3type Foo struct{}
 4
 5// Call 方法会接收由API层转发,路由为/example/call的HTTP请求
 6func (e *Example) Call(ctx context.Context, req *proto.CallRequest, rsp *proto.CallResponse) error {
 7    log.Print("收到 Example.Call 请求")
 8
 9    if len(req.Name) == 0 {
10        return errors.BadRequest("go.micro.api.example", "no content")
11    }
12
13    rsp.Message = "RPC Call收到了你的请求 " + req.Name
14    return nil
15}
16
17// Bar 方法会接收由API层转发,路由为/example/foo/bar的HTTP请求
18// 该接口我们什么参数也不处理,只打印信息
19func (f *Foo) Bar(ctx context.Context, req *proto.EmptyRequest, rsp *proto.EmptyResponse) error {
20    log.Print("收到 Foo.Bar 请求")
21    return nil
22}

编写我们的主函数

 1func main() {
 2    service := micro.NewService(
 3        micro.Name("go.micro.api.example"),
 4    )
 5
 6    service.Init()
 7
 8    // 注册 example 接口
 9    proto.RegisterExampleHandler(service.Server(), new(Example))
10
11    // 注册 foo 接口
12    proto.RegisterFooHandler(service.Server(), new(Foo))
13
14    if err := service.Run(); err != nil {
15        log.Fatal(err)
16    }
17}

现在测试我们的代码

以rpc模式运行API

1micro api --handler=rpc

运行服务端代码

1go run rpc.go

使用postman进行测试

微服务系列笔记之RPC和WebSocket

WebSocket模式

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

微服务系列笔记之RPC和WebSocket

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

 1<!DOCTYPE html>
 2<html>
 3<head>
 4    <meta charset="utf-8"/>
 5    <title>Websocket Stream</title>
 6    <script src="./main.js"></script>
 7    <style>
 8        table {
 9            table-layout: fixed;
10        }
11        td {
12            border: 2px solid green;
13        }
14        td input {
15            width: 100%;
16            box-sizing: border-box;
17        }
18    </style>
19</head>
20<body>
21<h2>Websocket Stream</h2>
22<table>
23    <tr>
24        <td valign="top" width="25%">
25            <p>
26                <form>
27            <p> Name: <br>
28                <input id="name" name="name" type="text"/>
29            </p>
30            <p>
31                <button type="button" id="send">Send</button>
32                <button type="button" id="cancel">Cancel</button>
33            </p>
34            <p>
35                <button type="button" id="open">Open Connection</button>
36            </p>
37            </form>
38            </p>
39        </td>
40        <td valign="top" width="75%">
41            <div id="output"/>
42        </td>
43    </tr>
44</table>
45</body>
46</html>

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

onopen:监听与服务端连接成功执行代码

onclose:监听与服务端断开时执行相应代码

onmessage:监听收到服务端信息时执行代码

onerror:监听与服务端通信期间出现了错误,执行相应代码

 1window.addEventListener("load", function (evt) {
 2    var wsUri = "ws://localhost:8080/websocket"
 3    var output = document.getElementById("output");
 4    var nameTxt = document.getElementById("name");
 5    var ws;
 6
 7    var print = function (message) {
 8        var d = document.createElement("div");
 9        d.innerHTML = message;
10        output.appendChild(d);
11    };
12
13    (function () {
14        ws = new WebSocket(wsUri);
15
16        ws.onopen = function (evt) {
17            print('<span style="color: green;">Connection Open</span>');
18        }
19        ws.onclose = function (evt) {
20            print('<span style="color: red;">Connection Closed</span>');
21            ws = null;
22        }
23        ws.onmessage = function (evt) {
24            print('<span style="color: blue;">Update: </span>' + evt.data);
25        }
26        ws.onerror = function (evt) {
27            print('<span style="color: red;">Error: </span>' + evt.data);
28        }
29    })();
30
31
32    document.getElementById("send").onclick = function (evt) {
33        if (!ws) {
34            return false
35        }
36
37        var msg = {hi: nameTxt.value}
38
39        req = JSON.stringify(msg)
40        print('<span style="color: blue;">Sent request: </span>' + req);
41        ws.send(JSON.stringify(msg));
42
43        return false;
44    };
45
46    document.getElementById("cancel").onclick = function (evt) {
47        if (!ws) {
48            return false;
49        }
50        ws.close();
51        print('<span style="color: red;">Request Canceled</span>');
52        return false;
53    };
54
55    document.getElementById("open").onclick = function (evt) {
56        if (!ws) {
57            newSocket()
58        }
59        return false;
60    };
61})

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

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

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

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

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

 1func hi(w http.ResponseWriter, r *http.Request) {
 2
 3    c, err := upGrader.Upgrade(w, r, nil)
 4    if err != nil {
 5        log.Printf("upgrade: %s", err)
 6        return
 7    }
 8
 9    defer c.Close()
10    for {
11        mt, message, err := c.ReadMessage()
12        if err != nil {
13            log.Println("read:", err)
14            break
15        }
16
17        log.Printf("recv: %s", message)
18
19        err = c.WriteMessage(mt, message)
20        if err != nil {
21            log.Println("write:", err)
22            break
23        }
24    }
25}

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

 1service := web.NewService(
 2    web.Name("go.micro.web.websocket"),
 3)
 4    if err := service.Init(); err != nil {
 5    log.Fatal("Init", err)
 6}
 7// static files
 8service.Handle("/websocket/", http.StripPrefix("/websocket/", http.FileServer(http.Dir("html"))))
 9// websocket interface
10service.HandleFunc("/websocket", hi)
11if err := service.Run(); err != nil {
12log.Fatal("Run: ", err)
13}

现在我们运行测试一下

1micro api --handler=web --namespace=go.micro.web
2go run ./web.go

打开浏览器输入

微服务系列笔记之RPC和WebSocket
微服务系列笔记之RPC和WebSocket

END

今日推荐阅读

RabbitMQ系列笔记广播模式和路由模式  

RabbitMQ系列笔记入门篇

RabbitMQ系列笔记work模式

RabbitMQ系列笔记work模式

protoc语法详解及结合grpc定义服务

Golang中Model的使用

基于Nginx和Consul构建高可用及自动发现的Docker服务架构

微服务系列笔记之RPC和WebSocket

▼关注我,一起成长

主要分享 学习心得、笔记、随笔▼

微服务系列笔记之RPC和WebSocket

微服务系列笔记之RPC和WebSocket
原文  http://mp.weixin.qq.com/s?__biz=MzA4NTIyOTI3NQ==&mid=2247483900&idx=1&sn=9060591e0d6c5c0fd45c29da21f8e62e
正文到此结束
Loading...