- PVSM.RU - https://www.pvsm.ru -

Пара недочётов в создании веб приложения на Go

Если вам интересно, какой недочёт есть во всех вебприложениях на языке Go размещенных на хабре. И хотите знать, как сделать своё Go вебприложение на один шаг ближе к production ready, то добро пожаловать по кат.

Важное отличие веб программирования на языке Go это то, что в результате вы обычно получаете программу, которая является вебсервером. Поэтому вы становитесь ответственными за то, что обычно отвечает вебсервер.

Если сравнить как выглядят Hello World на PHP и Go, то мы увидим

PHP

<?php echo “Hello World”; ?>

Go

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Взято с вики [1] сайта языка Go

Аналогичный код используют, в следующих статьях на хабре: Веб-разработка на Go [2], Пишем веб-эмулятор терминала на Go, используя Websocket [3], Goblog: Самодельный статический движок для блога на Go [4], Написание своего Web-приложения на Go [5].

Близко по теме: Go Language. Небольшое клиент-серверное приложение [6], TCP/IP proxy на Go [7].

Важным отличием и преимуществом Go является то, как Go работает с сетью и может держать множество открытых соединений, например, для реализации long polling.

В статье C10k (Проблема 10000 соединений) на разных языках/платформах [8] Go показал второй результат после Erlang, открыв 9775 соодинений из 10000.

Вторым преимуществом Go — является простота и читаемость кода, легкость в изучении, что делает его часто выбором №1 при разработке приложений, в которых нужно работать со множеством открытых соединений.

Ошибка Недочёт всех этих примеров

Недочёт в том, что Timeout (Deadline) для соединений по умолчанию 0 (т.е. timeout вовсе не установлен). Если клиент не отправит пакет о том, что соединение закрыто, то такое соединение повиснет на всегда. Это приведет к блокировке goroutine в режиме ожидания, до конца жизни вебсервера. Количество открытых соединение ресурс обычно ограниченный. По умолчанию в Линуксе приложение может открыть 1024 файла (TCP соединение приравнено к файлу).

Это значит — сервер созданный на Go следующим образом

http.ListenAndServe(":8080", nil)

Будет постепенно накапливать не закрытые соединения и захлебнется ими через день, месяц или год.

Поэтому важно устанавливать Timeout, которые в обычных вебсерверах установлены по умолчанию.

Например

	// log.Fatal(http.ListenAndServe(":8085", nil))
	{
		s := &http.Server{
			Addr:           ":8085",
			Handler:        nil,
			ReadTimeout:    1000 * time.Second, 
			WriteTimeout:   1000 * time.Second,
			MaxHeaderBytes: 1 << 20,
		}

		log.Fatal(s.ListenAndServe())
	} 

Второй недочёт параметр, который в обычном вебсервере часто указывается это максимальное количество соединений, которое вебсервер готов открыть. В случаи достижения этого ограничения вебсервер начинает жертвовать keep-alive соединениями или выдавать ошибку и закрывать соединения.

Go по умолчанию такого не делает, и вообще не очевидно как в Go ограничить количество соединений или посчитать количество соединений или тем более управлять этим процессом.

Что бы разобраться с тем, как управлять соединениями нужно заглянуть в исходники func (*Server) ListenAndServe [9] и func (*Server) ListenAndServeTLS [10], тогда мы увидим, что оби функции используют функцию func (*Server) Serve [11].

Эта функция получает интерфейс net.Listener как аргумент.
Вот его мы и можем реализовать для ограничения и контроля соединений.
Пример обвертки вокруг интерфейса net.Listener является LimitListener [12]

Пройдя по статьям на хабре с тегом [go] [13] я не нашел примеров в, которых устанавливались Timeout, DeadLine или контролировалось количество соединений.

Если учесть, что Go создавался как сверхпараллельный, сетевой язык, то реализация правильной и продуманной стратегии в работе соединениями, является обязательным элементом приложения на Go.

Автор: pyra

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/vy-sokaya-proizvoditel-nost/45606

Ссылки в тексте:

[1] вики: http://golang.org/doc/articles/wiki/

[2] Веб-разработка на Go: http://habrahabr.ru/post/122095/

[3] Пишем веб-эмулятор терминала на Go, используя Websocket: http://habrahabr.ru/post/141068/

[4] Goblog: Самодельный статический движок для блога на Go: http://habrahabr.ru/post/142287/

[5] Написание своего Web-приложения на Go: http://habrahabr.ru/post/183508/

[6] Go Language. Небольшое клиент-серверное приложение: http://habrahabr.ru/post/126461/

[7] TCP/IP proxy на Go: http://habrahabr.ru/post/142527/

[8] C10k (Проблема 10000 соединений) на разных языках/платформах: http://habrahabr.ru/post/145796/

[9] func (*Server) ListenAndServe: http://golang.org/src/pkg/net/http/server.go?s=45295:45336#L1513

[10] func (*Server) ListenAndServeTLS: http://golang.org/src/pkg/net/http/server.go?s=48707:48775#L1632

[11] func (*Server) Serve: http://golang.org/pkg/net/http/#Server.Serve

[12] LimitListener: https://code.google.com/p/go/source/browse/netutil/listen.go?repo=net

[13] [go]: http://habrahabr.ru/search/?q=%5Bgo%5D

[14] Источник: http://habrahabr.ru/post/197468/