How to implement a high-concurrency network programming framework in Go language

PHPz
Release: 2023-04-06 10:44:17
Original
1132 people have browsed it

In realizing high-concurrency network programming, the Workerman framework of PHP language has always been known for its excellent performance and simplicity and ease of use. However, compared to the PHP language, Golang is more suitable for high concurrency and distributed system development, so implementing the Golang version of the workerman framework has become the pursuit of many developers. In this article, we will introduce how to use the Golang language to implement a high-concurrency network programming framework, similar to Workerman.

1. Prerequisite knowledge

Before we start, we need to master some basic knowledge:

1.Golang language basics: variables, functions, structures, interfaces, etc. concept.

2. Network programming basics: basic knowledge of TCP/UDP, HTTP and other protocols.

3.Goroutine: The coroutine of Golang language can greatly improve the efficiency of concurrent programming.

4.Channel: A communication mechanism provided by the Golang language, which can be used for data transmission and synchronization between different coroutines.

5.Select: A multiplexing mechanism provided by the Golang language, which can monitor the status of multiple Channels and improve program efficiency.

2. Framework architecture

According to the implementation of the Workerman framework, we can divide it into three parts:

1. Receive the connection and generate the client.

2. Business process used to process client requests.

3. Monitor the client connection status and recycle it.

In the Golang language, we can use goroutine to implement the above three parts respectively.

1. Receive the connection and generate the client

We can use the "net" package that comes with the Golang language to create a TCP server, and at the same time open a goroutine to monitor the client connection status.

import (
  "fmt"
  "net"
)

func main() {
  listener, err := net.Listen("tcp", "127.0.0.1:8080")
  if err != nil {
    fmt.Println("failed to listen:", err)
    return
  }

  go func() {
    for {
      conn, err := listener.Accept()
      if err != nil {
        fmt.Println("failed to accept:", err)
        continue
      }
      // 生成客户端
    }
  }()

  // 等待进程退出
  select {}
}
Copy after login

After receiving the client connection, we need to encapsulate a Client object to handle all requests and responses for the connection.

type Client struct {
  Conn   net.Conn
  RespCh chan []byte
}

func NewClient(conn net.Conn) *Client {
  return &Client {
    Conn:   conn,
    RespCh: make(chan []byte, 10),
  }
}
Copy after login

2. Business process used to process client requests

The client's requests and responses are delivered directly through the Channel. When a new connection is received, we need to encapsulate it into a Client object and open a goroutine to handle the connection. This goroutine will listen to all requests sent by the client through the Channel and respond accordingly.

We encapsulate the business process into a Handler interface.

type Handler interface {
  OnConnect(*Client) error
  OnMessage(*Client, []byte) error
  OnClose(*Client) error
}
Copy after login

The client's request and response are passed through the RespCh attribute of the Client object. Therefore, in the Handler interface, we need to define a RespCh property to receive the response from the client.

type Handler interface {
  OnConnect(*Client) error
  OnMessage(*Client, []byte) error
  OnClose(*Client) error
  RespCh() chan []byte
}
Copy after login

We can create an EchoHandler to implement the Handler interface.

type EchoHandler struct {
  clients   []*Client
  respChan  chan []byte
}

func NewEchoHandler() *EchoHandler {
  return &EchoHandler{
    clients:  make([]*Client, 0),
    respChan: make(chan []byte, 10),
  }
}

func (h *EchoHandler) OnConnect(c *Client) error {
  h.clients = append(h.clients, c)
  return nil
}

func (h *EchoHandler) OnMessage(c *Client, data []byte) error {
  // 将客户端发送的数据广播给所有其他客户端,并将其存入respChan中
  for _, client := range h.clients {
    if client == c {
      continue
    }
    client.RespCh <- data
  }
  return nil
}

func (h *EchoHandler) OnClose(c *Client) error {
  for index, client := range h.clients {
    if client == c {
      h.clients = append(h.clients[:index], h.clients[index+1:]...)
    }
  }
  return nil
}

func (h *EchoHandler) RespCh() chan []byte {
  return h.respChan
}
Copy after login

When the Client objects of each connected client are stored in the clients array, we can receive the data sent by each client through the RespCh attribute and broadcast the information sent by the client to other clients. Client functionality.

3. Monitor the client connection status and recycle it

For the old version of the Workerman framework, Workerman will recycle idle connections within a certain period of time. The new version of Workerman implements this function through TCP keepalive.

When implementing the Golang version of workererman, we can also solve the idle connection problem through TCP keepalive. We can monitor the status of its socket in the goroutine of each client. If a client does not send data after 10 seconds of idle time, it will be regarded as an illegal connection and its socket will be closed.

func (c *Client) Process() {
  defer func() {
    c.Conn.Close()
    c.handler.OnClose(c)
  }()
  // 设置 socket keepalive
  tcpConn, ok := c.Conn.(*net.TCPConn)
  if ok {
    tcpConn.SetKeepAlive(true)
    tcpConn.SetKeepAlivePeriod(10 * time.Second)
  }
  // 进入读协程,接收客户端发送的所有数据
  go func() {
    for {
      buf := make([]byte, 1024)
      n, err := c.Conn.Read(buf)
      if err != nil {
        if err != io.EOF {
          fmt.Println("failed to read:", err)
        }
        break
      }
      // 将客户端发送的消息交给Handler处理
      c.handler.OnMessage(c, buf[:n])
    }
  }()
  // 进入写协程,将respChan中的所有响应发送给当前客户端
  go func() {
    for resp := range c.handler.RespCh() {
      _, err := c.Conn.Write(resp)
      if err != nil {
        fmt.Println("failed to write:", err)
        break
      }
    }
  }()
  // OnConnect
  err := c.handler.OnConnect(c)
  if err != nil {
    fmt.Println("failed to on connect:", err)
    return
  }
  // 在Worker进程退出时进行清理
  select {}
}
Copy after login

3. Implement the Worker process

After completing the above three steps, we need to create a Worker process to manage all client connections. One or more Handlers need to be loaded in the Worker process to handle all data requests sent by clients.

type Worker struct {
  listener  net.Listener
  handlers  map[string]Handler
}

func NewWorker(addr string) (*Worker, error) {
  listener, err := net.Listen("tcp", addr)
  if err != nil {
    fmt.Println("failed to listen:", err)
    return nil, err
  }
  return &Worker{
    listener: listener,
    handlers: make(map[string]Handler),
  }, nil
}

func (w *Worker) Register(name string, handler Handler) {
  w.handlers[name] = handler
}

func (w *Worker) Start() {
  go func() {
    for {
      conn, err := w.listener.Accept()
      if err != nil {
        fmt.Println("failed to accept:", err)
        continue
      }
      // 封装连接客户端为Client对象,用于后续的处理
      client := NewClient(conn)
      client.handler = w.handlers["Echo"]
      // 开启客户端goroutine来处理该连接
      go client.Process()
    }
  }()
  // 等待进程退出
  select {}
}
Copy after login

In the Worker process, we need to define a handlers attribute to store instances of different Handlers, and monitor client connections in the Start() function and start new goroutines to handle client requests.

4. Test

We can use the following code to create a Worker process and register an EchoHandler in it to handle all client requests.

func main() {
  server, _ := NewWorker("127.0.0.1:8080")
  handler := NewEchoHandler()
  server.Register("Echo", handler)
  server.Start()
}
Copy after login

We can use the telnet tool to simulate multiple clients sending messages to the server and view their reception.

We use the following command to connect to the server:

telnet 127.0.0.1 8080
Copy after login

We can enter the following text in telnet:

Hello workerman!
Copy after login

We can open multiple telnet windows at the same time to simulate multiple Client requests in parallel.

On the server, we can see the output:

$ go run worker.go 
服务器已启动...
failed to read: read tcp 127.0.0.1:8080->127.0.0.1:56182: use of closed network connection
Copy after login

This is because when we close the client connection, we cancel its listening operation, resulting in a read error.

After the telnet input is completed, we can see that each telnet window will receive the text returned by the server.

5. Summary

In this article, we introduced how to use the Golang language to implement a highly concurrent network programming framework, similar to the workerman in the PHP language. During the implementation process, we used the coroutine, communication mechanism and multiplexing mechanism in the Golang language, and successfully implemented a high-concurrency network programming framework similar to Workerman by encapsulating the Client object and Handler interface.

In fact, in daily programming, we recommend directly using the net/http package provided by the Golang language to implement high-concurrency network programming, which is more concise and has better performance than the workerman framework. We only need to open an http server and use goroutine to process each request concurrently to easily implement high-concurrency network programming.

The above is the detailed content of How to implement a high-concurrency network programming framework in Go language. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!