Golang实现简单HTTP服务器

Golang实现简单HTTP服务器

设计思想

一图胜千言:
1bcc6ee379374835.png 并发模型是,只要有client来访问就开一个goroutine去处理,goroutine之间
不需要通信。

实现的功能

  • 并发处理浏览器请求。
  • 日志模块。
  • 配置模块。

代码结构

.
├── README.md
├── README.org
├── deps                          # 安装第三方库脚本
├── install                       # 编译脚本
└── src                           # 源代码
    ├── config                    # 配置模块
    │   └── config.go
    ├── github.com                # 第三方库
    ├── gohttpserver              # main模块
    │   └── gohttpserver.go
    ├── logger                    # 日志模块
    │   └── logger.go
    ├── request                   # request 模块
    │   ├── request.go
    │   └── request_test.go
    └── response                  # response 模块
        ├── response.go
        └── response_test.go.

关键代码

// 处理client的goroutine
func (ghs *GoHttpServer) handleClient(conn net.Conn) {
        reqChans := request.RequestsChans(conn)
        response.StartResponse(conn, reqChans)
}

// 服务器永远运行,只要有client就用goroutine去处理
func (ghs *GoHttpServer) ServerForever() {
        for {
                conn, err := ghs.listener.AcceptTCP()
                if err != nil {
                        logger.Logger.Warning(
                                "Accept Client connection error, error msg %s",
                                err.Error(),
                        )
                        continue
                }
                timeout := time.Second * time.Duration(config.SerConfig.Timeout)
                conn.SetDeadline(time.Now().Add(timeout))
                go ghs.handleClient(conn)
        }
}

遇到的问题

如何处理Http/1.1中的Keep-Alive?

解决:使用channel来当作request队列,response模块从队列读取request信息返回。

// return http request channels
func RequestsChans(conn net.Conn) chan *Request {
        reader := bufio.NewReader(conn)
        reqCap := config.SerConfig.ReqChanCap
        reqChans := make(chan *Request, reqCap)

        go func() {
                reqSlice := make([]string, reqCap)
                for {
                        line, err := reader.ReadString('\n')
                        if err == nil {
                                line = strings.TrimSpace(line)
                                reqSlice = append(reqSlice, line)
                                if len(line) == 0 {
                                        req := parseRequest(reqSlice)
                                        loggerReqInfo(conn, req)
                                        reqChans <- req
                                        reqSlice = reqSlice[:0]
                                }
                        } else {
                                logger.Logger.Debug(
                                        "Ip %s connection close, close msg %s",
                                        conn.RemoteAddr().String(),
                                        err.Error(),
                                )
                                conn.Close()
                                break
                        }
                }
                close(reqChans)
        }()
        return reqChans
}

完整代码请参照https://bitbucket.org/runforever/gohttpserver/overview