206 lines
4.5 KiB
Markdown
206 lines
4.5 KiB
Markdown
|
|
# TCP 适配器快速开始指南
|
|||
|
|
|
|||
|
|
## 简介
|
|||
|
|
|
|||
|
|
TCP 适配器提供了一个无需 Pulsar 的 Watermill 消息发布/订阅实现,适用于内网直连场景。
|
|||
|
|
|
|||
|
|
## 快速开始
|
|||
|
|
|
|||
|
|
### 1. 启动消费端(Subscriber)
|
|||
|
|
|
|||
|
|
消费端作为 TCP 服务器,监听指定端口。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"log"
|
|||
|
|
|
|||
|
|
"go.yandata.net/iod/iod/trustlog-sdk/api/adapter"
|
|||
|
|
"go.yandata.net/iod/iod/trustlog-sdk/api/logger"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// 使用 NopLogger 或自定义 logger
|
|||
|
|
log := logger.NewNopLogger()
|
|||
|
|
|
|||
|
|
// 创建 Subscriber
|
|||
|
|
config := adapter.TCPSubscriberConfig{
|
|||
|
|
ListenAddr: "127.0.0.1:9090",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
subscriber, err := adapter.NewTCPSubscriber(config, log)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
defer subscriber.Close()
|
|||
|
|
|
|||
|
|
// 订阅 topic
|
|||
|
|
messages, err := subscriber.Subscribe(context.Background(), "my-topic")
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理消息
|
|||
|
|
for msg := range messages {
|
|||
|
|
log.Println("收到消息:", string(msg.Payload))
|
|||
|
|
msg.Ack() // 确认消息
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 启动生产端(Publisher)
|
|||
|
|
|
|||
|
|
生产端作为 TCP 客户端,连接到消费端。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/ThreeDotsLabs/watermill/message"
|
|||
|
|
"go.yandata.net/iod/iod/trustlog-sdk/api/adapter"
|
|||
|
|
"go.yandata.net/iod/iod/trustlog-sdk/api/logger"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
log := logger.NewNopLogger()
|
|||
|
|
|
|||
|
|
// 创建 Publisher
|
|||
|
|
config := adapter.TCPPublisherConfig{
|
|||
|
|
ServerAddr: "127.0.0.1:9090",
|
|||
|
|
ConnectTimeout: 5 * time.Second,
|
|||
|
|
AckTimeout: 10 * time.Second,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
publisher, err := adapter.NewTCPPublisher(config, log)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
defer publisher.Close()
|
|||
|
|
|
|||
|
|
// 发送消息
|
|||
|
|
msg := message.NewMessage("msg-001", []byte("Hello, World!"))
|
|||
|
|
|
|||
|
|
err = publisher.Publish("my-topic", msg)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log.Println("消息发送成功")
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 特性演示
|
|||
|
|
|
|||
|
|
### 并发发送多条消息
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 准备 10 条消息
|
|||
|
|
messages := make([]*message.Message, 10)
|
|||
|
|
for i := 0; i < 10; i++ {
|
|||
|
|
payload := []byte(fmt.Sprintf("Message #%d", i))
|
|||
|
|
messages[i] = message.NewMessage(fmt.Sprintf("msg-%d", i), payload)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 并发发送,Publisher 会等待所有 ACK
|
|||
|
|
err := publisher.Publish("my-topic", messages...)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log.Println("所有消息发送成功")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 错误处理和 NACK
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 在消费端
|
|||
|
|
for msg := range messages {
|
|||
|
|
// 处理消息
|
|||
|
|
if err := processMessage(msg); err != nil {
|
|||
|
|
log.Println("处理失败:", err)
|
|||
|
|
msg.Nack() // 拒绝消息
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
msg.Ack() // 确认消息
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 配置参数
|
|||
|
|
|
|||
|
|
### TCPPublisherConfig
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type TCPPublisherConfig struct {
|
|||
|
|
ServerAddr string // 必填: TCP 服务器地址,如 "127.0.0.1:9090"
|
|||
|
|
ConnectTimeout time.Duration // 连接超时,默认 10s
|
|||
|
|
AckTimeout time.Duration // ACK 超时,默认 30s
|
|||
|
|
MaxRetries int // 最大重试次数,默认 3
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### TCPSubscriberConfig
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type TCPSubscriberConfig struct {
|
|||
|
|
ListenAddr string // 必填: 监听地址,如 "127.0.0.1:9090"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 运行示例
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 运行完整示例
|
|||
|
|
cd trustlog-sdk/examples
|
|||
|
|
go run tcp_example.go
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 性能特点
|
|||
|
|
|
|||
|
|
- ✅ **低延迟**: 直接 TCP 连接,无中间件开销
|
|||
|
|
- ✅ **高并发**: 支持并发发送多条消息
|
|||
|
|
- ✅ **可靠性**: 每条消息都需要 ACK 确认
|
|||
|
|
- ⚠️ **无持久化**: 消息仅在内存中传递
|
|||
|
|
|
|||
|
|
## 适用场景
|
|||
|
|
|
|||
|
|
✅ **适合:**
|
|||
|
|
- 内网服务间直接通信
|
|||
|
|
- 开发和测试环境
|
|||
|
|
- 无需消息持久化的场景
|
|||
|
|
- 低延迟要求的场景
|
|||
|
|
|
|||
|
|
❌ **不适合:**
|
|||
|
|
- 需要消息持久化
|
|||
|
|
- 需要高可用和故障恢复
|
|||
|
|
- 公网通信(需要加密)
|
|||
|
|
- 需要复杂的路由和负载均衡
|
|||
|
|
|
|||
|
|
## 常见问题
|
|||
|
|
|
|||
|
|
### Q: 如何处理连接断开?
|
|||
|
|
|
|||
|
|
A: 当前版本连接断开后需要重新创建 Publisher。未来版本将支持自动重连。
|
|||
|
|
|
|||
|
|
### Q: 消息会丢失吗?
|
|||
|
|
|
|||
|
|
A: TCP 适配器不提供持久化,连接断开或服务重启会导致未确认的消息丢失。
|
|||
|
|
|
|||
|
|
### Q: 如何实现多个消费者?
|
|||
|
|
|
|||
|
|
A: 当前版本将消息发送到第一个订阅者。如需负载均衡,需要在应用层实现。
|
|||
|
|
|
|||
|
|
### Q: 支持 TLS 加密吗?
|
|||
|
|
|
|||
|
|
A: 当前版本不支持 TLS。未来版本将添加 TLS/mTLS 支持。
|
|||
|
|
|
|||
|
|
## 下一步
|
|||
|
|
|
|||
|
|
- 查看 [完整文档](TCP_ADAPTER_README.md)
|
|||
|
|
- 运行 [测试用例](tcp_integration_test.go)
|
|||
|
|
- 查看 [示例代码](../../examples/tcp_example.go)
|
|||
|
|
|