不忘初心,砥砺前行
作者 | 陌无崖
转载请联系授权
这一篇文章会陆续介绍Micro API中的rpc模式和websocket模式,废话不多说,阅读前要保持头脑清晰就可以了。
首先同样定义我们的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进行测试
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
打开浏览器输入
END
今日推荐阅读
RabbitMQ系列笔记广播模式和路由模式
RabbitMQ系列笔记入门篇RabbitMQ系列笔记work模式
RabbitMQ系列笔记work模式
protoc语法详解及结合grpc定义服务
Golang中Model的使用
基于Nginx和Consul构建高可用及自动发现的Docker服务架构
▼关注我,一起成长
主要分享 学习心得、笔记、随笔▼