>  기사  >  백엔드 개발  >  Go 언어로 동시성 네트워크 프로그래밍 프레임워크를 구현하는 방법

Go 언어로 동시성 네트워크 프로그래밍 프레임워크를 구현하는 방법

PHPz
PHPz원래의
2023-04-06 09:11:581103검색

고동시성 네트워크 프로그래밍을 구현하는 데 있어 PHP 언어의 Workerman 프레임워크는 뛰어난 성능과 단순성 및 사용 편의성으로 항상 알려져 왔습니다. 그러나 PHP 언어에 비해 Golang은 높은 동시성 및 분산 시스템 개발에 더 적합하므로 Workerman 프레임워크의 Golang 버전 구현은 많은 개발자의 추구가 되었습니다. 이 기사에서는 Golang 언어를 사용하여 Workerman과 유사한 높은 동시성 네트워크 프로그래밍 프레임워크를 구현하는 방법을 소개합니다.

1. 전제 지식

시작하기 전에 몇 가지 기본 지식을 숙지해야 합니다.

1. Golang 언어 기본: 변수, 함수, 구조, 인터페이스 등과 같은 기본 개념.

2. 네트워크 프로그래밍의 기초: TCP/UDP, HTTP 및 기타 프로토콜에 대한 기본 지식.

3.Goroutine: Golang 언어의 코루틴은 동시 프로그래밍의 효율성을 크게 향상시킬 수 있습니다.

4.Channel: Golang 언어에서 제공하는 통신 메커니즘으로, 서로 다른 코루틴 간의 데이터 전송 및 동기화에 사용할 수 있습니다.

5.Select: Golang 언어에서 제공하는 다중화 메커니즘으로, 여러 채널의 상태를 모니터링하고 프로그램 효율성을 향상시킬 수 있습니다.

2. 프레임워크 아키텍처

작업자 프레임워크 구현에 따라 세 부분으로 나눌 수 있습니다.

1. 연결을 수신하고 클라이언트를 생성합니다.

2. 클라이언트 요청을 처리하는 데 사용되는 비즈니스 프로세스.

3. 클라이언트 연결 상태를 모니터링하고 재활용하세요.

Golang 언어에서는 goroutine을 사용하여 위의 세 부분을 각각 구현할 수 있습니다.

1. 연결 수신 및 클라이언트 생성

Golang 언어와 함께 제공되는 "net" 패키지를 사용하여 TCP 서버를 생성하는 동시에 고루틴을 열어 클라이언트 연결 상태를 모니터링할 수 있습니다.

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 {}
}

클라이언트 연결을 받은 후 연결에 대한 모든 요청과 응답을 처리하기 위해 클라이언트 개체를 캡슐화해야 합니다.

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

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

2. 클라이언트 요청을 처리하는 데 사용되는 비즈니스 프로세스

클라이언트의 요청과 응답은 채널을 통해 직접 전달됩니다. 새로운 연결이 수신되면 이를 Client 객체로 캡슐화하고 연결을 처리하기 위한 고루틴을 시작해야 합니다. 이 고루틴은 클라이언트가 채널을 통해 보낸 모든 요청을 듣고 그에 따라 응답합니다.

비즈니스 프로세스를 핸들러 인터페이스로 캡슐화합니다.

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

클라이언트의 요청과 응답은 클라이언트 개체의 RespCh 속성을 통해 전달됩니다. 따라서 Handler 인터페이스에서는 클라이언트로부터 응답을 받기 위해 RespCh 속성을 정의해야 합니다.

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

EchoHandler를 생성하여 핸들러 인터페이스를 구현할 수 있습니다.

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
}

연결된 각 클라이언트의 Client 객체가 클라이언트 배열에 저장되면 RespCh 속성을 통해 각 클라이언트가 보낸 데이터를 수신할 수 있고, 클라이언트가 보낸 정보를 다른 클라이언트에게 브로드캐스팅하는 기능을 구현할 수 있습니다.

3. 클라이언트 연결 상태를 모니터링하고 재활용합니다.

이전 버전의 Workerman 프레임워크에서는 Workerman이 일정 기간 내에 유휴 연결을 재활용합니다. Workerman의 새 버전은 TCP keepalive를 통해 이 기능을 구현합니다.

workererman의 Golang 버전을 구현할 때 TCP keepalive를 통해 유휴 연결 문제를 해결할 수도 있습니다. 각 클라이언트의 고루틴에서 소켓 상태를 모니터링할 수 있습니다. 클라이언트가 유휴 시간 10초 후에도 데이터를 보내지 않으면 잘못된 연결로 간주되어 소켓이 닫힙니다.

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 {}
}

3. 작업자 프로세스 구현

위의 세 단계를 완료한 후 모든 클라이언트 연결을 관리하기 위한 작업자 프로세스를 만들어야 합니다. 클라이언트가 보낸 모든 데이터 요청을 처리하려면 작업자 프로세스에 하나 이상의 핸들러를 로드해야 합니다.

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 {}
}

Worker 프로세스에서는 다양한 핸들러의 인스턴스를 저장하기 위한 핸들러 속성을 정의해야 하며, Start() 함수에서 클라이언트 연결을 수신하고 클라이언트 요청을 처리하기 위해 새 고루틴을 시작해야 합니다.

4. Test

다음 코드를 사용하여 작업자 프로세스를 생성하고 여기에 EchoHandler를 등록하여 모든 클라이언트 요청을 처리할 수 있습니다.

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

텔넷 도구를 사용하여 서버에 메시지를 보내는 여러 클라이언트를 시뮬레이션하고 수신을 볼 수 있습니다.

다음 명령을 사용하여 서버에 연결합니다.

telnet 127.0.0.1 8080

텔넷에 다음 텍스트를 입력할 수 있습니다.

Hello workerman!

여러 텔넷 창을 동시에 열어 여러 클라이언트 병렬 요청을 시뮬레이션할 수 있습니다.

서버에서 다음 출력을 볼 수 있습니다.

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

이는 클라이언트 연결을 닫을 때 수신 작업을 취소하여 읽기 오류가 발생하기 때문입니다.

텔넷 입력이 완료되면 각 텔넷 창이 서버에서 반환된 텍스트를 수신하는 것을 볼 수 있습니다.

5. 요약

이 글에서는 Golang 언어를 사용하여 PHP 언어의 워커맨과 유사한 동시성 네트워크 프로그래밍 프레임워크를 구현하는 방법을 소개했습니다. 구현 과정에서 우리는 Golang 언어의 코루틴, 통신 메커니즘 및 다중화 메커니즘을 사용했으며 클라이언트 객체 및 핸들러 인터페이스를 캡슐화하여 Workerman과 유사한 높은 동시성 네트워크 프로그래밍 프레임워크를 성공적으로 구현했습니다.

실제로 일상적인 프로그래밍에서는 Golang 언어에서 제공하는 net/http 패키지를 직접 사용하여 워커맨 프레임워크보다 더 간결하고 성능이 뛰어난 동시성 네트워크 프로그래밍을 구현하는 것이 좋습니다. 높은 동시성 네트워크 프로그래밍을 쉽게 구현하려면 http 서버를 열고 goroutine을 사용하여 각 요청을 동시에 처리하면 됩니다.

위 내용은 Go 언어로 동시성 네트워크 프로그래밍 프레임워크를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.