Fibril is a Go-based WebSocket library built on top of GoFiber that provides a robust and efficient way to handle WebSocket connections, messaging, and subscriptions. It supports features like message broadcasting, client management, and publishing messages to subscribed topics.
- 🚀 High-performance WebSocket server
- 🔗 Built-in Pub/Sub system
- ⚡ Sharding support for better scalability
- 🔒 Client management with UUIDs
- 🗂️ Custom key-value storage per client
- 🧪 Monitoring:
ListTopics()andSubscriberCount(topic)let you introspect pub/sub usage in real-time
go get -u github.com/lishank0119/fibrilpackage main
import (
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
"github.com/lishank0119/fibril"
"log"
)
func main() {
app := fiber.New()
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
return c.Next()
}
return fiber.ErrUpgradeRequired
})
f := fibril.New(
fibril.WithShardCount(4),
fibril.WithMaxMessageSize(1024),
)
f.TextMessageHandler(func(client *fibril.Client, msg string) {
log.Printf("Received message from %s: %s", client.GetUUID(), msg)
f.BroadcastText("Echo: " + msg)
})
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
f.RegisterClient(c)
}))
log.Fatal(app.Listen(":3000"))
}Iterates over all active WebSocket clients and applies the given callback.
f.ForEachClient(func(uuid string, client *fibril.Client) {
// handle each client
})Iterates over all active clients and cancels early if the context is done.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
f.ForEachClientWithContext(ctx, func(uuid string, client *fibril.Client) {
// handle each client
})Retrieves a connected WebSocket client by UUID.
client, ok := f.GetClient("your-client-uuid")
if ok {
client.SendText("Hello!")
}The number of active WebSocket clients connected to the hub.
f.ClientLen()Publishes a message to a specific topic.
err := f.Publish("server-time", []byte("2024-02-09T15:04:05Z"))
if err != nil {
log.Println("Publish error:", err)
}Sends a text message to a specific client.
err := f.SendTextToClient("client-uuid", "Hello, Client!")
if err != nil {
log.Println("Send error:", err)
}Sends binary data to a specific client.
err := f.SendBinaryToClient("client-uuid", []byte{0x01, 0x02, 0x03})
if err != nil {
log.Println("Send error:", err)
}Broadcasts a text message to all connected clients.
f.BroadcastText("Hello, everyone!")Broadcasts binary data to all connected clients.
f.BroadcastBinary([]byte{0x10, 0x20, 0x30})Registers a new WebSocket client.
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
f.RegisterClient(c)
}))Registers a new WebSocket client with custom key-value pairs.
app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {
f.RegisterClientWithKeys(c, map[any]any{"id": c.Params("id")})
}))Disconnects all connected WebSocket clients with a given close message.
f.DisconnectAll("Server restarting")Disconnects a specific client by UUID.
err := f.DisconnectClient("Goodbye!", "client-uuid")
if err != nil {
log.Println("Disconnect error:", err)
}Disconnects clients based on a filter function.
f.DisconnectClientFilter("Maintenance", func(c *fibril.Client) bool {
return c.GetKey("role") == "guest"
})- ConnectHandler: Handles client connection.
f.ConnectHandler(func(client *fibril.Client) {
log.Println("Client connected:", client.GetUUID())
})- DisconnectHandler: Handles client disconnection.
f.DisconnectHandler(func(client *fibril.Client) {
log.Println("Client disconnected:", client.GetUUID())
})- ErrorHandler: Handles errors that occur within a client connection.
f.ErrorHandler(func(client *fibril.Client, err error) {
log.Println("Error for client", client.GetUUID(), ":", err)
})- TextMessageHandler: Handles text messages.
f.TextMessageHandler(func(client *fibril.Client, msg string) {
log.Println("Received text message:", msg)
})- BinaryMessageHandler: Handles binary messages.
f.BinaryMessageHandler(func(client *fibril.Client, msg []byte) {
log.Println("Received binary message:", msg)
})- PongHandler: Handles pong responses from clients.
f.PongHandler(func(client *fibril.Client) {
log.Println("Pong received from:", client.GetUUID())
})Gets the unique identifier (UUID) of the client.
uuid := client.GetUUID()
log.Printf("Client UUID: %s", uuid)Subscribes the client to a specific topic with a handler function.
client.Subscribe("topic-name", func(msg []byte) {
log.Printf("Received message for topic: %s", string(msg))
})Sends a text message to the client.
err := client.SendText("Hello, Client!")
if err != nil {
log.Println("Send error:", err)
}Sends binary data to the client.
err := client.SendBinary([]byte{0x01, 0x02, 0x03})
if err != nil {
log.Println("Send error:", err)
}Disconnects the client with a custom message.
client.Disconnect("Goodbye!")Stores a custom key-value pair associated with the client.
client.StoreKey("role", "admin")Deletes a custom key-value pair associated with the client.
client.DeleteKey("role")Retrieves the value of a key associated with the client.
role, ok := client.GetKey("role")
if ok {
log.Printf("Client role: %v", role)
}You can customize the following options when initializing Fibril:
- ShardCount: The number of shards for managing clients (default: 16).
- MaxMessageSize: The maximum size of incoming messages in bytes (default: 512).
- MessageBufferSize: The size of the message buffer (default: 256).
- WriteWait: The duration to wait before closing the write connection (default: 10 seconds).
- PongWait: The duration to wait for a pong response from the client (default: 60 seconds).
- PingPeriod: The interval to send ping messages (default: 54 seconds).
f := fibril.New(
fibril.WithShardCount(20),
fibril.WithMaxMessageSize(1024),
fibril.WithMessageBufferSize(512),
fibril.WithWriteWait(15 * time.Second),
fibril.WithPongWait(30 * time.Second),
fibril.WithPingPeriod(25 * time.Second),
)Fibril exposes methods to monitor internal pub/sub state:
fibril.ListTopics() // returns []string
fibril.SubscriberCount("X") // returns intFeel free to contribute to the project by forking it, making improvements, or submitting bug fixes via pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.