对应 examples/stream 例子:
这个例子, 有 一个 streaming 服务, 和两个 client, 一个是 streaming rpc , 一个是 js websockets + web services (streaming rpc)。
目录:
server 是 服务
client : 是 rpc streaming client
web : 是 websocket handler (和浏览器js websocket组成 peer) + rpc streaming client
server/proto/stream.proto 内容如下:
syntax = "proto3";
service Streamer {
rpc Stream(stream Request) returns (stream Response){}
rpc ServerStream(Request) returns (stream Response) {}
}
message Request {
int64 count = 1;
}
message Response {
int64 count = 1;
}
protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. stream.proto
server/main.go 代码如下:
package main
import (
"context"
"github.com/micro/go-micro"
"io"
"log"
proto "zhaozhiliang.com/stream/server/proto"
)
type Streamer struct{}
//服务 方 的stream
func (e *Streamer) ServerStream(ctx context.Context, req *proto.Request, stream proto.Streamer_ServerStreamStream) error {
log.Printf("Got msg %v", req.Count)
for i := 0; i< int(req.Count); i++ {
if err := stream.Send(&proto.Response{Count: int64(i)}); err != nil {
return err
}
}
return nil
}
//双向的 stream
func (e *Streamer) Stream(ctx context.Context, stream proto.Streamer_StreamStream) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
log.Printf("Got msg %v", req.Count)
if err := stream.Send(&proto.Response{Count: req.Count}); err != nil {
return err
}
}
}
func main() {
service := micro.NewService(
micro.Name("go.micro.srv.stream"),
)
service.Init()
proto.RegisterStreamerHandler(service.Server(), new(Streamer))
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
client/main.go 代码如下:
package main
import (
"context"
"fmt"
"github.com/micro/go-micro"
proto "zhaozhiliang.com/stream/server/proto"
)
//双向
func bidirectional(cl proto.StreamerService) {
//创建 流 client
stream, err := cl.Stream(context.Background())
if err != nil {
fmt.Println("err:", err)
return
}
// 双向流,
//发送 和 接受 messages 10次
for j := 0; j < 10; j++ {
if err := stream.Send(&proto.Request{Count: int64(j)}); err != nil {
fmt.Println("err:", err)
}
rsp, err := stream.Recv()
if err != nil {
fmt.Println("recv err", err)
break
}
fmt.Printf("Sent msg %v got msg %v\n", j, rsp.Count)
}
//关闭 stream
if err := stream.Close(); err != nil {
fmt.Println("stream close err:", err)
}
}
func serverStream(cl proto.StreamerService) {
//发送请求
stream, err := cl.ServerStream(context.Background(), &proto.Request{Count: int64(10)})
if err != nil {
fmt.Println("err:", err)
return
}
//服务端 来的 stream
//接受到了 10个 消息
for j := 0; j < 10; j++{
rsp, err := stream.Recv()
if err != nil {
fmt.Println("recv err", err)
break
}
fmt.Printf("got msg %v\n", rsp.Count)
}
//关闭 stream
if err := stream.Close(); err != nil {
fmt.Println("stream close err:", err)
}
}
func main() {
service := micro.NewService()
service.Init()
//创建 client
cl := proto.NewStreamerService("go.micro.srv.stream", service.Client())
//双向 流
bidirectional(cl)
//服务 单方面 流
serverStream(cl)
}
web/main.go 代码如下:
package main
import (
"context"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/web"
"io"
"log"
"net/http"
"time"
proto "zhaozhiliang.com/stream/server/proto"
)
var upgrader = websocket.Upgrader{
//允许跨域
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func Stream (cli proto.StreamerService, ws *websocket.Conn) error {
// 从 websocket 读取 请求,并初始化
var req proto.Request
err := ws.ReadJSON(&req)
if err != nil {
return err
}
log.Printf("Got req.Count %v from websocket", req.Count)
//即便 我们不再 期望 来自websocket 更多的请求,我们仍需要 去 websocket 读取 内容,为了能获取到 close 信号
//???
go func() {
for {
if _, _, err := ws.NextReader(); err != nil {
break
}
}
}()
log.Printf("Received Request: %v", req)
//发送请求 给 stream server
stream, err := cli.ServerStream(context.Background(), &req)
if err != nil {
return err
}
defer stream.Close()
// 从 stream server 中读取消息,并且发送响应 给 websocket
for {
rsp, err := stream.Recv()
log.Printf("Got msg %v from services", rsp.Count)
if err != nil {
if err != io.EOF {
return err
}
break
}
// 写 response 给 websocket
err = ws.WriteJSON(rsp)
if err != nil {
if isExpectedClose(err) {
log.Println("Expected Close on socket", err)
break
} else {
return err
}
}
}
return nil
}
func isExpectedClose(err error) bool {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway){
log.Println("Unexpected websocket close:", err)
return false
}
return true
}
func main() {
//新建 web service
service := web.NewService(
web.Name("go.micro.web.stream"),
)
if err := service.Init(); err != nil {
log.Fatal("Init", err)
}
//新建 RPC client
rpcClient := client.NewClient(client.RequestTimeout(time.Second * 120))
cli := proto.NewStreamerService("go.micro.srv.stream", rpcClient)
//获取 静态文件 html/js
service.Handle("/", http.FileServer(http.Dir("html")))
//处理 websocket 连接
service.HandleFunc("/stream", func( w http.ResponseWriter, r *http.Request) {
//升级请求到 websocket
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal("Upgrade:", err)
}
defer conn.Close()
//处理 websocket 请求
if err := Stream(cli, conn); err != nil {
log.Fatal("Echo :", err)
}
log.Println("stream complete")
})
if err := service.Run(); err != nil {
log.Fatal("Run: ", err)
}
}
web/html/index.html 代码如下:
Websocket Stream
Websocket Stream
web/html/main.js 代码如下:
var wsUri;
var output;
var count;
var ws;
window.addEventListener("load", function (evt) {
wsUri = "ws://" + window.location.host + "/stream/stream"
output = document.getElementById("output");
count = document.getElementById("count");
var print = function (message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
}
var parseCount = function (evt) {
return JSON.parse(evt.data).count
}
var newSocket = function () {
ws = new WebSocket(wsUri);
ws.onopen = function (evt) {
print(' Connection Open');
}
ws.onclose = function (evt) {
print(' Connection Closed');
ws = null;
}
ws.onmessage = function (evt) {
print('Update:' + parseCount(evt));
}
ws.onerror = function (evt) {
print('Error:' + parseCount(evt));
}
};
newSocket()
document.getElementById("send").onclick = function (evt) {
if (!ws) {
return false
}
var msg = {count: parseInt(count.value)}
req = JSON.stringify(msg)
print('Sent request: ' + req);
ws.send(JSON.stringify(msg))
return false;
}
document.getElementById("cancel").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
print('Request Canceled');
return false;
};
document.getElementById("open").onclick = function(evt) {
if (!ws) {
newSocket()
}
return false;
};
})
go.mod 代码如下:
module zhaozhiliang.com/stream
go 1.13
require (
github.com/golang/protobuf v1.3.2
github.com/micro/go-micro v1.18.0
)
运行程序:
运行 :
运行服务 : go run/server/main.go
运行micro web (它也可以为 websocket client 提供反向代理):micro web
运行 websocket client : cd web; go run main.go
运行client: go run client/main.go
访问 http://192.168.1.129:8082/stream 并 发送一个 请求。
本例子架构图: