Penstriman log masa nyata dalam Go

王林
Lepaskan: 2024-07-23 12:30:24
asal
844 orang telah melayarinya

Hampir simulasi ekor -f tetapi dengan cara yang menarik.

Mari kita atasi masalah ini dengan membahagikannya kepada tugas yang boleh diurus, memberikan penjelasan yang jelas untuk setiap langkah. Kami akan mulakan dengan gambaran keseluruhan dan kemudian menyelidiki setiap tugasan.

Ikhtisar

  1. Pemantauan Fail: Pantau fail log secara berterusan untuk kandungan yang baru ditambah.
  2. Persediaan Pelayan: Wujudkan pelayan untuk mengendalikan sambungan pelanggan masuk dan menyiarkan mesej.
  3. Pengendalian Sambungan Pelanggan: Urus sambungan dan pemutusan sambungan pelanggan.
  4. Penyiaran Mesej: Siarkan entri log yang baru ditambah kepada semua pelanggan yang disambungkan.
  5. Pengujian dan Pengoptimuman: Pastikan penyelesaiannya cekap dan teguh.

Pecahan Tugas

1 - Pemantauan Fail
Matlamat: Sediakan mekanisme untuk memantau fail log untuk penambahan baharu dalam masa nyata.
Langkah:

  • Gunakan pakej os untuk membaca dan memantau fail.
  • Baca fail secara berterusan dari kedudukan terakhir yang diketahui.
  • Kesan dan baca kandungan yang baru dilampirkan.

Pelaksanaan:

package main

import (
    "os"
    "time"
    "io"
    "log"
)

func tailFile(filePath string, lines chan<- string) {
    file, err := os.Open(filePath)
    if err != nil {
        log.Fatalf("failed to open file: %s", err)
    }
    defer file.Close()

    fi, err := file.Stat()
    if err != nil {
        log.Fatalf("failed to get file stats: %s", err)
    }

    // Start reading from end of file
    file.Seek(0, io.SeekEnd)
    offset := fi.Size()

    for {
        // Check the file size
        fi, err := file.Stat()
        if err != nil {
            log.Fatalf("failed to get file stats: %s", err)
        }

        if fi.Size() > offset {
            // Seek to the last position
            file.Seek(offset, io.SeekStart)
            buf := make([]byte, fi.Size()-offset)
            _, err := file.Read(buf)
            if err != nil && err != io.EOF {
                log.Fatalf("failed to read file: %s", err)
            }

            lines <- string(buf)
            offset = fi.Size()
        }

        time.Sleep(1 * time.Second)
    }
}
Salin selepas log masuk

Fungsi ini akan membaca kandungan baharu daripada fail yang ditentukan dan menghantarnya ke saluran talian.

2- Persediaan Pelayan
Matlamat: Sediakan pelayan asas menggunakan Gorilla WebSocket untuk mengendalikan sambungan pelanggan.
Langkah:

  • Gunakan pakej github.com/gorilla/websocket.
  • Buat pelayan HTTP yang meningkatkan sambungan kepada WebSocket.

Pelaksanaan:

package main

import (
    "net/http"
    "github.com/gorilla/websocket"
    "log"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        // Allow all connections
        return true
    },
}

func handleConnections(w http.ResponseWriter, r *http.Request, clients map[*websocket.Conn]bool) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatalf("failed to upgrade connection: %s", err)
    }
    defer ws.Close()

    // Register the new client
    clients[ws] = true

    // Wait for new messages
    for {
        var msg string
        err := ws.ReadJSON(&msg)
        if err != nil {
            delete(clients, ws)
            break
        }
    }
}

func main() {
    clients := make(map[*websocket.Conn]bool)

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        handleConnections(w, r, clients)
    })

    log.Println("Server started on :8080")
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatalf("failed to start server: %s", err)
    }
}
Salin selepas log masuk

3- Pengendalian Sambungan Pelanggan
Matlamat: Urus sambungan pelanggan dan pemutusan sambungan, memastikan pengendalian yang mantap.
Langkah:

  • Kekalkan peta pelanggan aktif.
  • Tambah dan alih keluar pelanggan dengan selamat.

Pelaksanaan:

package main

var clients = make(map[*websocket.Conn]bool)

func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("error upgrading to websocket: %v", err)
        return
    }
    defer ws.Close()

    clients[ws] = true

    for {
        _, _, err := ws.ReadMessage()
        if err != nil {
            delete(clients, ws)
            break
        }
    }
}
Salin selepas log masuk

4- Penyiaran Mesej
Matlamat: Siarkan baris log baharu kepada semua pelanggan yang disambungkan.
Langkah:

  • Baca dari saluran talian.
  • Siarkan kepada semua pelanggan yang berkaitan.

Pelaksanaan:

package main

func broadcastMessages(lines <-chan string, clients map[*websocket.Conn]bool) {
    for {
        msg := <-lines
        for client := range clients {
            err := client.WriteMessage(websocket.TextMessage, []byte(msg))
            if err != nil {
                client.Close()
                delete(clients, client)
            }
        }
    }
}
Salin selepas log masuk

5- Integrasi dan Pengoptimuman
Matlamat: Mengintegrasikan semua komponen dan mengoptimumkan prestasi.
Langkah:

  • Gabungkan pemantauan fail, persediaan pelayan dan penyiaran mesej.
  • Tambahkan mekanisme kawalan serentak yang sesuai (saluran, mutex).

Dalam langkah ini, kami akan menyepadukan pemantauan fail log, persediaan pelayan, pengendalian sambungan pelanggan dan fungsi penyiaran mesej ke dalam satu atur cara yang padu. Kami juga akan menambah mekanisme kawalan serentak untuk memastikan keselamatan dan keteguhan benang.

Penyepaduan Kod Penuh

package main

import (
    "log"
    "net/http"
    "os"
    "sync"
    "time"

    "github.com/gorilla/websocket"
)

// Upgrade configuration
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        // Allow cross-origin requests
        return true
    },
}

var (
    clients = make(map[*websocket.Conn]bool) // Map to store all active clients
    mu      sync.Mutex                       // Mutex to ensure thread safety
)

// handleConnections handles incoming websocket connections.
func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("error upgrading to websocket: %v", err)
        return
    }
    defer ws.Close()

    mu.Lock()
    clients[ws] = true
    mu.Unlock()

    // Keep the connection open
    for {
        if _, _, err := ws.ReadMessage(); err != nil {
            mu.Lock()
            delete(clients, ws)
            mu.Unlock()
            ws.Close()
            break
        }
    }
}

// broadcastMessages reads from the lines channel and sends to all clients.
func broadcastMessages(lines <-chan string) {
    for {
        msg := <-lines
        mu.Lock()
        for client := range clients {
            err := client.WriteMessage(websocket.TextMessage, []byte(msg))
            if err != nil {
                client.Close()
                delete(clients, client)
            }
        }
        mu.Unlock()
    }
}

// tailFile watches the given file for changes and sends new lines to the lines channel.
func tailFile(filePath string, lines chan<- string) {
    file, err := os.Open(filePath)
    if err != nil {
        log.Fatalf("failed to open file: %v", err)
    }
    defer file.Close()

    fi, err := file.Stat()
    if err != nil {
        log.Fatalf("failed to get file stats: %v", err)
    }

    // Start reading from end of file
    file.Seek(0, io.SeekEnd)
    offset := fi.Size()

    for {
        fi, err := file.Stat()
        if err != nil {
            log.Fatalf("failed to get file stats: %v", err)
        }

        if fi.Size() > offset {
            // Seek to the last position
            file.Seek(offset, io.SeekStart)
            buf := make([]byte, fi.Size()-offset)
            _, err := file.Read(buf)
            if err != nil && err != io.EOF {
                log.Fatalf("failed to read file: %v", err)
            }

            lines <- string(buf)
            offset = fi.Size()
        }

        time.Sleep(1 * time.Second)
    }
}

// main function to start the server and initialize goroutines.
func main() {
    lines := make(chan string)

    go tailFile("test.log", lines)       // Start file tailing in a goroutine
    go broadcastMessages(lines)         // Start broadcasting messages in a goroutine

    http.HandleFunc("/ws", handleConnections) // Websocket endpoint

    log.Println("Server started on :8080")
    err := http.ListenAndServe(":8080", nil) // Start HTTP server
    if err != nil {
        log.Fatalf("Failed to start server: %v", err)
    }
}
Salin selepas log masuk

Image description

Penerangan Kod:

Pemantauan Fail:

  • Fungsi tailFile dijalankan dalam goroutine, memantau fail log secara berterusan untuk kandungan baharu dan menghantar baris baharu ke saluran (baris).

Persediaan Pelayan:

  • Pelayan HTTP disediakan dengan http.HandleFunc("/ws", handleConnections) yang meningkatkan sambungan HTTP kepada WebSockets menggunakan pustaka Gorilla WebSocket.

Pengendalian Pelanggan:

  • Pelanggan dikendalikan dalam handleConnections. Sambungan dinaik taraf kepada WebSocket dan setiap sambungan diuruskan dalam peta yang dipanggil klien.
  • Mutex (mu) digunakan untuk memastikan keselamatan benang semasa menambah atau mengalih keluar pelanggan.

Penyiaran Mesej:

  • Fungsi broadcastMessages membaca dari saluran talian dan menghantar kandungan kepada semua pelanggan yang disambungkan.
  • Fungsi ini berjalan dalam goroutine sendiri dan menggunakan mutex untuk memastikan keselamatan benang apabila mengakses peta pelanggan.

Integrasi dan Pengoptimuman:

  • Semua komponen disepadukan dan dijalankan serentak menggunakan goroutine.
  • Penyegerakan dikendalikan dengan mutex untuk memastikan operasi pada peta pelanggan selamat untuk benang.

Menjalankan Program

1- Simpan kod dalam fail, contohnya, main.go.
2- Pastikan anda memasang pakej Gorilla WebSocket:

go get github.com/gorilla/websocket
Salin selepas log masuk

3- Jalankan program Go:

go run main.go
Salin selepas log masuk

4- Gunakan klien WebSocket untuk menyambung ke ws://localhost:8080/ws.

  • Mencipta klien WebSocket boleh dilakukan menggunakan pelbagai alat dan kaedah. Di bawah, saya akan memberikan arahan dan contoh untuk mencipta klien WebSocket menggunakan kedua-dua alat CLI (seperti websocat)
  • Menggunakan Alat CLI: websocat
  • websocat ialah klien WebSocket mudah untuk baris arahan. Anda boleh memasangnya dan menggunakannya untuk menyambung ke pelayan WebSocket anda.

Pemasangan:

  • On macOS, you can install websocat using Homebrew:
brew install websocat
Salin selepas log masuk
  • On Ubuntu, you can install it via Snap:
sudo snap install websocat
Salin selepas log masuk

You can also download the binary directly from the GitHub releases page.

Usage:

To connect to your WebSocket server running at ws://localhost:8080/ws, you can use:

websocat ws://localhost:8080/ws
Salin selepas log masuk

Type a message and hit Enter to send it. Any messages received from the server will also be displayed in the terminal.

WebSockets are a widely used protocol for real-time, bidirectional communication between clients and servers. However, they do come with some limitations. Let's discuss these limitations and explore some alternatives that might be more suitable depending on the use case.

Limitations of Using WebSocket

Scalability: While WebSockets are effective for low to moderate traffic, scaling to handle a large number of concurrent connections can be challenging. This often requires sophisticated load balancing and infrastructure management.

State Management: WebSockets are stateful, which means each connection maintains its own state. This can become complicated when scaling horizontally because you need to ensure that sessions are properly managed across multiple servers (e.g., using sticky sessions or a distributed session store).

Resource Intensive: Each WebSocket connection consumes server resources. If you have many clients, this can rapidly consume memory and processing power, necessitating robust resource management.

Firewalls and Proxies: Some corporate firewalls and proxy servers block WebSocket connections because they don’t conform to the traditional HTTP request-response model. This can limit the accessibility of your application.

Security: Although WebSockets can be used over encrypted connections (wss://), they can still be vulnerable to attacks such as cross-site WebSocket hijacking (CSWSH). Ensuring robust security measures is essential.

Latency: While WebSockets have low latency, they are not always the best option for applications that require ultra-low latency or where the timing of messages is critical.

Alternatives to WebSocket

1- Server-Sent Events (SSE)

SSE is a standard allowing servers to push notifications to clients in a unidirectional stream over HTTP.
It is simpler to implement than WebSockets and works natively in many browsers without requiring additional libraries.
Use Cases:

Real-time updates like live feeds, notifications, or social media updates where the data flow is largely unidirectional (server to client).

  • Pros:
    Simpler protocol and easier to implement.
    Built-in reconnection logic.
    Less resource-intensive than WebSockets for unidirectional data flow.

  • Cons:
    Unidirectional (server-to-client) only.
    Less suitable for applications requiring bi-directional communication.

Example:

const eventSource = new EventSource('http://localhost:8080/events');

eventSource.onmessage = function(event) {
    console.log('New message from server: ', event.data);
};
Salin selepas log masuk

2- HTTP/2 and HTTP/3

The newer versions of HTTP (HTTP/2 and HTTP/3) support persistent connections and multiplexing, which can effectively simulate real-time communication.
They include features like server push, which allows the server to send data to clients without an explicit request.

Use Cases:
When you need to improve the performance and latency of web applications that already use HTTP for communication.

  • Pros:
    Improved performance and lower latency due to multiplexing.
    Better support and broader compatibility with existing HTTP infrastructure.

  • Cons:
    Requires updating server infrastructure to support HTTP/2 or HTTP/3.
    More complex than HTTP/1.1.

3- WebRTC

WebRTC (Web Real-Time Communication) is a technology designed for peer-to-peer communication, primarily for audio and video streaming.
It can also be used for real-time data transfer.

Use Cases:
Real-time audio and video communication.
Peer-to-peer file sharing or live streaming.

  • Pros:
    Peer-to-peer connections reduce server load.
    Built-in support for NAT traversal and encryption.

  • Cons:
    More complex to implement than WebSockets or SSE.
    Requires good understanding of signaling and peer connection management.

4- Message Brokers (e.g., MQTT, AMQP)

Protocols like MQTT and AMQP are designed for message queuing and are optimized for different use cases.
MQTT is lightweight and commonly used in IoT devices.
AMQP is more robust and feature-rich, suited for enterprise-level messaging.

Kes Penggunaan:
Aplikasi IoT.
Sistem teragih yang memerlukan penghantaran mesej yang boleh dipercayai.
Aplikasi dengan penghalaan kompleks dan keperluan baris gilir mesej.

  • Kebaikan:
    Teguh dan kaya dengan ciri (terutamanya AMQP).
    Sesuai untuk rangkaian yang tidak boleh dipercayai dan terhad (terutamanya MQTT).

  • Keburukan:
    Memperkenalkan kerumitan infrastruktur tambahan.
    Memerlukan pelayan broker mesej dan biasanya lebih banyak persediaan.

Ringkasan

Bergantung pada keperluan khusus anda, WebSockets mungkin masih menjadi pilihan yang baik. Walau bagaimanapun, jika anda menghadapi had dari segi kebolehskalaan, kerumitan atau kesesuaian, mempertimbangkan salah satu alternatif seperti Peristiwa Dihantar Pelayan (SSE), HTTP/2/3, WebRTC atau broker mesej khusus seperti MQTT atau AMQP mungkin lebih sesuai. . Setiap alternatif ini mempunyai kekuatan sendiri dan senario penggunaan terbaik, dan memahami perkara ini akan membantu anda memilih teknologi yang paling sesuai untuk aplikasi anda.

Atas ialah kandungan terperinci Penstriman log masa nyata dalam Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan