# gowebsocket: a Go WebSocket IM reference for one machine and a million connections

> gowebsocket is a Go chat system built on gorilla/websocket, Gin, gRPC and Redis, written as a tutorial for engineers who want to see how a horizontally deployable IM server is put together. The repository is the source of truth; the README is the manual.

**link1st/gowebsocket** — golang基于websocket单台机器支持百万连接分布式聊天(IM)系统

- Repository: https://github.com/link1st/gowebsocket
- Stars: 3,110 · Forks: 637
- Language: Go
- License: NOASSERTION
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/link1st-gowebsocket

## The problem gowebsocket addresses: server-initiated delivery to many sockets

HTTP requests start at the client. If a chat backend has to notify an online user, polling is the only option without a persistent channel, and the README lists exactly this as the motivation: chat, task completion notices, and operational messages to users who happen to be online. gowebsocket exists to demonstrate a Go server that keeps those channels open, tracks which users are online, and lets one node push to a user whose socket lives on another node.

The target reader is a Go engineer who has used Gin and wants to see the whole path from protocol upgrade to cross-node delivery in one repository. It is not a hosted service and it is not a library you import. The README frames the project as an article plus a runnable codebase, and the repository layout matches that: controllers, routers, servers, protobuf and views sit side by side with the README. The million-connection claim is a design target the README explains and then qualifies with a stress-test chapter; treat it as the shape of the system, not as a number you inherit by deploying it.

## How a connection is upgraded, registered and routed inside the Go server

The mechanism is visible in the README's code walkthrough. The main function starts the WebSocket listener in a goroutine, and the listener registers an /acc handler on port 8089. That handler upgrades the HTTP request with gorilla/websocket's Upgrader, whose CheckOrigin function is shown printing the User-Agent and Referer headers before returning a decision.

After the upgrade, the README recommends two goroutines per connection: one reads from the client, one writes to it. The reason given is that separating read and write reduces the chance that sending blocks receiving. The read goroutine registers an asynchronous handler, the write goroutine is registered separately, and incoming frames are dispatched through a route table rather than a single switch, so new message types can be added as routes. Two chapters are dedicated to the failure mode this design invites: memory growth and goroutines that never exit. The README treats closing the socket, unregistering the client and stopping both goroutines as part of the design, not as cleanup you add later.

Cross-node delivery is where gRPC enters. A node that holds a socket for user A can be asked by another node to deliver a message, and the README documents RPC methods for checking whether a user is online, sending a message, broadcasting to a room, and listing the users in a room. Redis appears in go.mod through github.com/redis/go-redis/v9, which is consistent with the README's description of nodes communicating internally and sharing state; the README does not spell out the full key layout, so anyone extending the online-user registry should read the models and servers packages first.

## Installing gowebsocket and running it for the first time

The repository ships a Makefile with a single target, and the README points readers at the project download and experience sections rather than a package manager. Clone the repository, then use the Makefile to start the process:

```bash
make run
```

That target runs `go run main.go`, which is the same command you can type directly. The WebSocket listener is registered on port 8089, so a client connects to that port after the process starts. The README's own example server code shows the registration:

```go
func StartWebSocket() {
	http.HandleFunc("/acc", wsPage)
	http.ListenAndServe(":8089", nil)
}
```

For a browser client, the README documents the standard upgrade handshake and notes that the JavaScript side registers listeners and then sends data. The handshake headers it quotes include `Connection: Upgrade`, `Upgrade: websocket` and `Sec-WebSocket-Version: 13`; a successful upgrade returns `Status Code: 101 Switching Protocols`. The README also links a live demo at http://im.20jd.com/home/index, which is useful for seeing the intended behaviour before you run anything locally.

Behind nginx, the README devotes a chapter to the proxy configuration and a following chapter to problems encountered with it. That ordering is a hint: the upgrade path through a reverse proxy is where most first deployments break, so configure nginx before you point a real client at the service.

## The dependencies you inherit, and the licence you do not

go.mod pins Gin v1.9.1, gorilla/websocket v1.5.3, go-redis v9.0.3, grpc v1.83.2, protobuf v1.36.11 and viper at a 2019 pseudo-version. The viper pin is the one to look at twice: `v1.4.1-0.20190728125013-1b33e8258e07` is a commit from July 2019, and configuration loading sits on the startup path. It works, but it is the dependency most likely to need attention if you upgrade the module, and the README does not discuss configuration migration.

The licence is the larger gap. The repository metadata reports NOASSERTION, and a LICENSE file exists at the top level. That combination means automated tooling could not classify the file, not that the project is unlicensed. If you plan to fork gowebsocket into a commercial product, open LICENSE and read it before you write code against the interfaces. Nothing here is legal advice, and the README does not restate the licence terms.

## Where the million-connection claim stops being useful

The README's stress-test chapter starts with Linux kernel tuning before it reports any numbers, which is the honest part of the claim: the socket count depends on file descriptors, kernel parameters and available memory, not on the Go code alone. Two goroutines per connection also means two goroutines per connection in the scheduler, and the README's own chapter on preventing memory overflow and unreclaimed goroutines exists because that cost is real.

The sharper limitation is scope. The README describes online presence, room membership and message delivery. It does not describe offline message storage, delivery acknowledgement, message ordering guarantees across nodes, or history retrieval. For a chat product those are not extras; they are the parts users notice when they fail. The HTTP interface list covers room user lists, online checks, sending to one user and sending to everyone, and the RPC list covers the same operations across nodes. Nothing in that list persists a message for a user who is offline.

Finally, the release history. The latest release is v2.0.2 from 2021-07-27, while the last push to master was on 2026-09-21. Code is moving; tagged releases are not. If you depend on versioned artifacts, you are depending on a tag that is years old.

## gowebsocket against a plain gorilla/websocket server

The obvious alternative is not another IM framework; it is gorilla/websocket on its own. gowebsocket uses that library for the upgrade and then adds the parts a single-process example leaves out: a route table for message types, a client registry, room membership, an HTTP surface for queries, and a gRPC surface so a second node can reach a socket it does not own.

That difference decides the choice. If you are writing one server that pushes notifications to its own clients, a bare gorilla/websocket handler plus your own map is smaller and has fewer moving parts. The moment you run two instances behind nginx and a user's socket may live on either one, you need the cross-node call and the shared presence state, and that is the problem gowebsocket is built around. The cost of taking it is Gin, gRPC, protobuf and Redis in your dependency graph, plus the viper pin noted above. The cost of not taking it is writing the registry and the RPC layer yourself, which is precisely the code the README walks through.

## What to verify before you build on it

Start with the two chapters that document interfaces: the HTTP endpoints and the RPC methods. They define the contract your clients and other services will use, and they are more stable than the internal packages. Then read the stress-test chapter's kernel tuning section, because the tuning it lists is a prerequisite for the connection counts the project is known for, and skipping it will make your own measurements meaningless.

After that, run the distributed deployment described in the README's architecture chapter and watch what happens when the node holding a user's socket is asked to deliver a message from the other node. That is the single behaviour the whole design exists to provide, and it is the one worth confirming on your own hardware. The README does not document a rollback procedure for a failed deployment, so plan your own before you put it behind traffic.

## Conclusion

Adopt gowebsocket if you want a readable Go WebSocket server to study or to fork into an internal IM prototype, and if you accept that the last release is v2.0.2 from 2021-07-27. Do not adopt it as a drop-in product if you need offline message storage, message acknowledgement or a documented rollback path; the README does not describe them. Before committing, read the RPC and HTTP interface sections, check the LICENSE file, and run the two-process deployment described under the distributed section to confirm that the gRPC path between nodes behaves as the README says.

## FAQ

### Are WebSockets deprecated?

The README states the WebSocket protocol was created in 2008, became an international standard in 2011, and that all browsers now support it. It documents the upgrade handshake and the 101 Switching Protocols response as the current mechanism, and does not describe any deprecation.

### What are the downsides of using WebSockets?

The README addresses this indirectly: it recommends two goroutines per connection to keep reading and writing from blocking each other, and it devotes a chapter to preventing memory overflow and goroutines that are never reclaimed. It also opens the stress-test chapter with Linux kernel tuning, which indicates the connection count depends on host configuration rather than the Go code alone.

### What is WebSocket used for in gowebsocket?

The README lists server-initiated notification to clients, chat systems, informing users when a task completes, reaching online users during operations, and reading user online status. It contrasts this with HTTP, where the client must initiate every request.

## Sources

- [Issues](https://github.com/link1st/gowebsocket/issues)
- [link1st/gowebsocket on GitHub](https://github.com/link1st/gowebsocket)
- [README](https://github.com/link1st/gowebsocket/blob/master/README.md)
- [Releases](https://github.com/link1st/gowebsocket/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/link1st-gowebsocket
