Golang实现redis系列-(2)基础的TCP
思路
redis服务器是tcp连接,这里我们先写一个基础的TCP服务器,而redis的hanler具体实现今后再写,这里仅实现了监听信号关闭服务,以及优雅的关闭(清理已经有的连接)
至于为什么只写了一个handler.Close(),是因为设计打算把所有的conn放在一个map里面,handler.Close()遍历map关闭所有连接
测试单元只是简单的写写,本篇博文重点是基础的TCP以及优雅的关闭
代码
package TCP import ( "Gotosp/pkg/log" "context" "net" "os" "os/signal" "sync" "syscall" ) type Handler interface { Handle(ctx context.Context, conn net.Conn) Close() } type ServerConfig struct { Address string } func StartServer(cfg *ServerConfig, handler Handler) error { ctx, cancel := context.WithCancel(context.Background()) sigCh := make(chan os.Signal) signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) //监听这些信号 go func() { <-sigCh cancel() //收到退出信号,告诉下流开始退出 signal.Stop(sigCh) close(sigCh) }() listener, err := net.Listen("tcp4", cfg.Address) if err != nil { return err } ListenAndServeWithSignal(ctx, listener, handler) return nil } func ListenAndServeWithSignal(ctx context.Context, listener net.Listener, handler Handler) { wg := sync.WaitGroup{ } //收到退出通知,开始退出 go func() { <-ctx.Done() listener.Close() handler.Close() }() //意外panic导致程序退出 defer func() { listener.Close() handler.Close() }() for { conn, err := listener.Accept() if err != nil { log.Error("Accept err", err) break } wg.Add(1) go func() { defer func() { wg.Done() }() handler.Handle(ctx, conn) }() } wg.Wait() }
package TCP import ( "Gotosp/pkg/log" "bufio" "context" "io" "net" "sync" "testing" ) type echo struct { activeConn sync.Map conn net.Conn } func (e *echo) Handle(ctx context.Context, conn net.Conn) { e.conn = conn e.activeConn.Store(e.conn, struct{ }{ }) reader := bufio.NewReader(conn) for { msg, err := reader.ReadString( ) if err != nil { if err != io.EOF { log.Error("read err", err) } e.activeConn.Delete(e.conn) conn.Close() return } b := []byte(msg) conn.Write(b) } } func (e *echo) Close() { e.activeConn.Range(func(key interface{ }, val interface{ }) bool { client := key.(*echo) client.conn.Close() return true }) } func MakeEcho() *echo { return &echo{ } } func TestStartServer(t *testing.T) { cfg := &ServerConfig{ Address: "127.0.0.1:6872"} err := StartServer(cfg, MakeEcho()) if err != nil { log.Fatal("start server err", err) } }
上一篇:
IDEA上Java项目控制台中文乱码