《Go 简易速速上手小册》第8章:网络编程(2024 最新版)(下)

简介: 《Go 简易速速上手小册》第8章:网络编程(2024 最新版)

《Go 简易速速上手小册》第8章:网络编程(2024 最新版)(上)+https://developer.aliyun.com/article/1487003


8.3 WebSocket 与 RPC - Go 语言的深海通信线

在Go语言的并发海洋中,WebSocket和RPC (Remote Procedure Call) 是两种深海通信线,允许我们跨越深渊,进行实时和跨服务的通信。

8.3.1 基础知识讲解

WebSocket

WebSocket提供了一个全双工通信渠道,允许客户端和服务器之间建立持久连接并实时交换数据。这对于需要实时功能的应用来说非常有用,比如在线聊天室、实时数据仪表板等。

在Go中,gorilla/websocket是一个流行的库,用于在HTTP服务器上添加WebSocket支持。

RPC

RPC允许客户端执行远程服务器上的函数就像是执行本地函数一样,隐藏了网络请求的复杂性。Go标准库中的net/rpc包提供了构建RPC系统的基础。

8.3.2 重点案例:实时聊天应用

在这个扩展案例中,我们将构建一个简单的实时聊天应用,使用WebSocket实现客户端和服务器之间的实时通信。这个应用将允许用户通过Web浏览器连接到聊天服务器,发送消息并实时接收来自其他用户的消息。

服务端实现

我们使用gorilla/websocket库来处理WebSocket连接。首先,需要安装这个库:

go get -u github.com/gorilla/websocket

然后,我们实现聊天服务器的核心逻辑:

// chatserver/main.go
package main
import (
    "github.com/gorilla/websocket"
    "net/http"
    "log"
)
var clients = make(map[*websocket.Conn]bool) // 连接到服务器的客户端
var broadcast = make(chan []byte)            // 广播通道
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}
func main() {
    http.HandleFunc("/ws", handleConnections)
    go handleMessages()
    log.Println("Chat server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()
    clients[ws] = true
    for {
        _, msg, err := ws.ReadMessage()
        if err != nil {
            log.Printf("Error: %v", err)
            delete(clients, ws)
            break
        }
        broadcast <- msg
    }
}
func handleMessages() {
    for {
        msg := <-broadcast
        for client := range clients {
            err := client.WriteMessage(websocket.TextMessage, msg)
            if err != nil {
                log.Printf("Error: %v", err)
                client.Close()
                delete(clients, client)
            }
        }
    }
}

在这个实现中,handleConnections函数处理新的WebSocket连接,读取来自客户端的消息,并将它们放入broadcast通道。handleMessages函数监听broadcast通道,将接收到的消息发送给所有连接的客户端。

客户端实现

客户端可以是一个简单的HTML页面,使用JavaScript与WebSocket服务器进行通信:

<!-- chatclient/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Chat App</title>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var ws = new WebSocket("ws://localhost:8080/ws");
            var messages = document.getElementById("messages");
            ws.onmessage = function(event) {
                var message = document.createElement("p");
                message.textContent = event.data;
                messages.appendChild(message);
            };
            document.getElementById("sendBtn").onclick = function() {
                var message = document.getElementById("messageInput").value;
                ws.send(message);
                document.getElementById("messageInput").value = "";
            };
        });
    </script>
</head>
<body>
    <div id="messages"></div>
    <input id="messageInput" type="text">
    <button id="sendBtn">Send</button>
</body>
</html>

这个HTML页面包含一个消息列表、一个文本输入框和一个发送按钮。当用户点击发送按钮时,当前的消息会通过WebSocket发送到服务器,并且清空输入框。当服务器通过WebSocket发送消息时,它们会被添加到页面的消息列表中。

运行示例

  1. 启动服务端:在chatserver目录下运行go run main.go,启动聊天服务器。
  2. 打开客户端:在浏览器中打开chatclient/index.html文件,连接到聊天服务器。

通过这个案例,我们展示了如何使用Go和WebSocket构建一个简单的实时聊天应用。这个应用能够让多个用户通过Web界面实时交换消息,体验到实时通信的魅力。随着你继续探索WebSocket和Go在网络编程方面的更多可能性,你将能够构建更加复杂和强大的实时Web应用。

8.3.3 拓展案例 1:股票行情实时更新服务

在这个案例中,我们将构建一个基于WebSocket的股票行情实时更新服务。该服务允许客户端通过WebSocket订阅特定的股票代码,并在股票价格发生变动时接收实时更新。

服务端实现

为了简化演示,我们假设股票价格的变动是随机模拟的。在实际应用中,你可能需要连接到真实的股市数据API来获取实时行情。

首先,安装gorilla/websocket库:

go get -u github.com/gorilla/websocket

接着,实现WebSocket服务端逻辑:

// stockservice/main.go
package main
import (
    "encoding/json"
    "github.com/gorilla/websocket"
    "log"
    "math/rand"
    "net/http"
    "time"
)
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}
type StockUpdate struct {
    Symbol string  `json:"symbol"`
    Price  float64 `json:"price"`
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()
    // 模拟股票代码列表
    stocks := []string{"AAPL", "GOOGL", "MSFT"}
    for {
        // 等待客户端消息并读取订阅的股票代码
        _, msg, err := ws.ReadMessage()
        if err != nil {
            log.Printf("error: %v", err)
            break
        }
        symbol := string(msg)
        // 如果订阅的股票代码在模拟的列表中,开始发送更新
        for _, s := range stocks {
            if s == symbol {
                for {
                    // 随机生成股票价格并发送
                    price := rand.Float64() * 1000
                    update := StockUpdate{Symbol: symbol, Price: price}
                    updateJSON, err := json.Marshal(update)
                    if err != nil {
                        log.Printf("error: %v", err)
                        break
                    }
                    if err := ws.WriteMessage(websocket.TextMessage, updateJSON); err != nil {
                        log.Printf("error: %v", err)
                        break
                    }
                    // 每隔一秒发送一次更新
                    time.Sleep(1 * time.Second)
                }
                break
            }
        }
    }
}
func main() {
    rand.Seed(time.Now().UnixNano())
    http.HandleFunc("/ws", handleConnections)
    log.Println("Stock service started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

客户端示例

客户端可以是一个简单的HTML页面,通过JavaScript与WebSocket服务器建立连接,并发送订阅请求:

<!-- stockclient/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Real-time Stock Updates</title>
    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const ws = new WebSocket("ws://localhost:8080/ws");
            ws.onopen = () => {
                console.log("Connected to the server");
                // 订阅AAPL股票的更新
                ws.send("AAPL");
            };
            ws.onmessage = (event) => {
                const stockUpdate = JSON.parse(event.data);
                console.log(`Stock update for ${stockUpdate.symbol}: $${stockUpdate.price.toFixed(2)}`);
            };
            ws.onerror = (error) => {
                console.log("WebSocket error: " + error.message);
            };
            ws.onclose = () => {
                console.log("Disconnected from the server");
            };
        });
    </script>
</head>
<body>
    <h1>Real-time Stock Updates</h1>
    <p>Open the console to view the stock updates.</p>
</body>
</html>

在这个HTML页面中,当页面加载完成后,客户端通过WebSocket连接到服务器并发送一个订阅请求(在此例中为"AAPL"股票)。然后,它将监听服务器发送的更新,并在控制台中显示更新的股票价格。

运行示例

  1. 启动服务端:在stockservice

录下运行go run main.go,启动股票行情更新服务。

  1. 打开客户端:在Web浏览器中打开stockclient/index.html文件,并查看浏览器控制台以接收实时的股票价格更新。

通过这个拓展案例,我们演示了如何使用WebSocket在Go中实现一个实时的股票行情更新服务。客户端可以订阅感兴趣的股票代码,并接收关于这些股票的实时价格更新,展现了WebSocket在构建实时通信应用中的强大能力。继续探索WebSocket和Go的其他网络编程特性,为你的应用带来更加丰富的实时交互体验。

8.3.4 拓展案例 2:远程系统监控

在这个案例中,我们将构建一个基于RPC的远程系统监控工具,允许管理员从中央服务器调用远程机器上的函数,以获取系统状态信息,如CPU使用率、内存使用等。这个工具将使用Go的net/rpc包来实现RPC通信。

功能描述

  1. 远程获取系统状态:允许管理员远程调用函数,获取目标机器的系统状态信息。
  2. 支持多种状态查询:支持查询CPU使用率、内存使用情况等不同类型的系统状态。
  3. 简单的认证机制:实现一个简单的认证机制,确保只有授权的用户可以查询系统状态。

服务端实现

首先,我们需要定义提供的远程调用方法和服务:

// monitorserver/main.go
package main
import (
    "errors"
    "net"
    "net/rpc"
    "runtime"
)
type Args struct {
    AuthToken string
}
type SystemStats struct {
    CPU    string
    Memory string
}
type Monitor int
func (t *Monitor) GetSystemStats(args *Args, reply *SystemStats) error {
    if args.AuthToken != "secret" {
        return errors.New("unauthorized")
    }
    // 模拟获取系统状态
    reply.CPU = "2.4 GHz"
    reply.Memory = "8 GB"
    return nil
}
func main() {
    monitor := new(Monitor)
    rpc.Register(monitor)
    rpc.HandleHTTP()
    l, err := net.Listen("tcp", ":1234")
    if err != nil {
        panic(err)
    }
    for {
        conn, err := l.Accept()
        if err != nil {
            continue
        }
        go rpc.ServeConn(conn)
    }
}

在这个实现中,我们定义了一个Monitor类型,它有一个方法GetSystemStats,用于远程获取系统状态。我们使用了一个简单的认证机制,通过检查传递的AuthToken来授权访问。

客户端实现

客户端将通过RPC调用服务端的GetSystemStats方法来获取系统状态:

// monitorclient/main.go
package main
import (
    "fmt"
    "log"
    "net/rpc"
)
type Args struct {
    AuthToken string
}
type SystemStats struct {
    CPU    string
    Memory string
}
func main() {
    client, err := rpc.DialHTTP("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("Dialing:", err)
    }
    args := &Args{"secret"}
    var reply SystemStats
    err = client.Call("Monitor.GetSystemStats", args, &reply)
    if err != nil {
        log.Fatal("Monitor error:", err)
    }
    fmt.Printf("CPU: %s\nMemory: %s\n", reply.CPU, reply.Memory)
}

在这个客户端实现中,我们创建了一个RPC客户端,连接到服务端,并调用Monitor.GetSystemStats方法,传递认证令牌并获取系统状态信息。

运行示例

  1. 启动服务端:在monitorserver目录下运行go run main.go启动系统监控服务。
  2. 运行客户端:在monitorclient目录下运行go run main.go来从服务端获取系统状态信息。

通过这个拓展案例,我们演示了如何使用Go的net/rpc包实现一个基于RPC的远程系统监控工具。这个工具允许管理员远程获取系统状态信息,展现了RPC在构建分布式系统和服务通信中的实用性。继续探索Go的RPC和其他网络编程特性,为你的分布式应用提供强大的后端支持。

目录
相关文章
|
3月前
|
数据采集 Go 定位技术
使用go并发网络爬虫
使用go并发网络爬虫
|
3月前
|
安全 Java Go
为什么选择Go语言编写网络应用程序
为什么选择Go语言编写网络应用程序
|
4月前
|
网络协议 算法 Go
在go内置网络库中的路由和多路复用
【7月更文挑战第6天】本文介绍Go的`net/http`库提供基础的HTTP服务,`ListenAndServe`管理TCP连接,处理请求。处理程序默认使用`DefaultServeMux`。也可以选择多路复用模式ServeMux。它们的示例代码展示了自定义`ServeHTTP`结构体处理不同路由 。
67 2
|
6月前
|
存储 JSON Go
《Go 简易速速上手小册》第8章:网络编程(2024 最新版)(上)
《Go 简易速速上手小册》第8章:网络编程(2024 最新版)
73 1
|
6月前
|
Kubernetes Cloud Native Go
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)(下)
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)
158 0
|
6月前
|
Cloud Native 算法 Go
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)(上)
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)
159 0
|
6月前
|
存储 SQL Go
《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)(下)
《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)
57 0
|
6月前
|
SQL 关系型数据库 Go
《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)(上)
《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)
38 0
|
1天前
|
编译器 Go
go语言编译选项
【10月更文挑战第17天】
9 5
|
4天前
|
安全 Go 开发者
go语言并发模型
【10月更文挑战第16天】
18 8