feat: 修复报错
This commit is contained in:
20
go-service/Dockerfile
Normal file
20
go-service/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
ARG DOCKER_MIRROR_PREFIX=m.daocloud.io/docker.io/library
|
||||
FROM ${DOCKER_MIRROR_PREFIX}/golang:1.22-bookworm
|
||||
|
||||
ENV GOPROXY=https://goproxy.cn,direct \
|
||||
GOSUMDB=sum.golang.google.cn \
|
||||
CGO_ENABLED=0
|
||||
|
||||
RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources \
|
||||
&& sed -i 's|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod ./
|
||||
RUN go mod tidy || true
|
||||
|
||||
COPY . .
|
||||
RUN go mod tidy
|
||||
RUN go build -o /bin/go-service main.go
|
||||
|
||||
CMD ["/bin/go-service"]
|
||||
3
go-service/go.mod
Normal file
3
go-service/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module ai-trading/go-service
|
||||
|
||||
go 1.22
|
||||
171
go-service/main.go
Normal file
171
go-service/main.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
binance "github.com/adshao/go-binance/v2"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type KlineMessage struct {
|
||||
Type string `json:"type"`
|
||||
Source string `json:"source"`
|
||||
Symbol string `json:"symbol"`
|
||||
Interval string `json:"interval"`
|
||||
EventTime int64 `json:"event_time"`
|
||||
OpenTime int64 `json:"open_time"`
|
||||
CloseTime int64 `json:"close_time"`
|
||||
Open string `json:"open"`
|
||||
High string `json:"high"`
|
||||
Low string `json:"low"`
|
||||
Close string `json:"close"`
|
||||
Volume string `json:"volume"`
|
||||
TradeNum int64 `json:"trade_num"`
|
||||
Final bool `json:"final"`
|
||||
}
|
||||
|
||||
type RedisPublisher interface {
|
||||
Publish(ctx context.Context, channel string, payload []byte) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type NoopPublisher struct{}
|
||||
|
||||
func (p *NoopPublisher) Publish(_ context.Context, _ string, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *NoopPublisher) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type RedisChannelPublisher struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
func (p *RedisChannelPublisher) Publish(ctx context.Context, channel string, payload []byte) error {
|
||||
return p.client.Publish(ctx, channel, payload).Err()
|
||||
}
|
||||
|
||||
func (p *RedisChannelPublisher) Close() error {
|
||||
return p.client.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
symbol := getEnv("BINANCE_SYMBOL", "btcusdt")
|
||||
interval := getEnv("BINANCE_INTERVAL", "1m")
|
||||
channel := getEnv("REDIS_CHANNEL", "kline.stream")
|
||||
|
||||
publisher, err := initPublisherFromEnv(ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("init publisher failed: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := publisher.Close(); err != nil {
|
||||
log.Printf("publisher close failed: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
doneC, stopC, err := binance.WsKlineServe(symbol, interval,
|
||||
func(event *binance.WsKlineEvent) {
|
||||
msg := KlineMessage{
|
||||
Type: "kline",
|
||||
Source: "binance_ws",
|
||||
Symbol: strings.ToLower(event.Symbol),
|
||||
Interval: event.Kline.Interval,
|
||||
EventTime: event.Time,
|
||||
OpenTime: event.Kline.StartTime,
|
||||
CloseTime: event.Kline.EndTime,
|
||||
Open: event.Kline.Open,
|
||||
High: event.Kline.High,
|
||||
Low: event.Kline.Low,
|
||||
Close: event.Kline.Close,
|
||||
Volume: event.Kline.Volume,
|
||||
TradeNum: int64(event.Kline.TradeNum),
|
||||
Final: event.Kline.IsFinal,
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
log.Printf("marshal failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(payload))
|
||||
|
||||
if err := publisher.Publish(ctx, channel, payload); err != nil {
|
||||
log.Printf("redis publish failed: %v", err)
|
||||
}
|
||||
},
|
||||
func(err error) {
|
||||
log.Printf("ws error: %v", err)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("start ws stream failed: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("subscribed binance kline stream: symbol=%s interval=%s channel=%s", symbol, interval, channel)
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-sig:
|
||||
log.Println("received shutdown signal")
|
||||
close(stopC)
|
||||
cancel()
|
||||
case <-doneC:
|
||||
log.Println("ws stream closed")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func initPublisherFromEnv(ctx context.Context) (RedisPublisher, error) {
|
||||
addr := strings.TrimSpace(os.Getenv("REDIS_ADDR"))
|
||||
if addr == "" {
|
||||
log.Println("REDIS_ADDR not configured, using NoopPublisher")
|
||||
return &NoopPublisher{}, nil
|
||||
}
|
||||
|
||||
db, err := strconv.Atoi(getEnv("REDIS_DB", "0"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid REDIS_DB: %w", err)
|
||||
}
|
||||
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Password: os.Getenv("REDIS_PASSWORD"),
|
||||
DB: db,
|
||||
DialTimeout: 5 * time.Second,
|
||||
ReadTimeout: 3 * time.Second,
|
||||
WriteTimeout: 3 * time.Second,
|
||||
})
|
||||
|
||||
if err := client.Ping(ctx).Err(); err != nil {
|
||||
return nil, fmt.Errorf("redis ping failed: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("connected redis at %s db=%d", addr, db)
|
||||
return &RedisChannelPublisher{client: client}, nil
|
||||
}
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
v := strings.TrimSpace(os.Getenv(key))
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
return v
|
||||
}
|
||||
Reference in New Issue
Block a user