Come work in Estonia – the most advanced digital society. Many Ukrainians already know that Estonia is affordable – become one of them and check out the jobs available!

Go — это просто. Создаем HelloWorld веб-сервер

По многочисленным просьбам читателей DOU публикую первую статью про Go.

Ниже будут раскрыты следующие темы:

  • как создать на Go простой веб-сервер, которому не нужны Apache с nginx’ом;
  • как добавить поддержку https без использования OpenSSL;
  • как перестать платить за TLS-сертификаты и беспокоиться об их своевременном обновлении.

Создаем HelloWorld веб-сервер на Go

Опустим разделы про установку и настройку Go. При желании можете почитать сами. Приступим сразу к делу :)

Создаем файл server.go и сохраняем в нем следующий код:

// Объявляем название пакета.
// Все *.go - файлы должны начинаться с названия пакета.
// Пакет с названием "main" имеет специальное назначение - он указывает
// компилятору go, что из этого пакета нужно собрать самодостаточный
// исполняемый файл. Это бинарник, которому для запуска не нужны дополнительные
// зависимости - его достаточно скопировать на нужный компьютер и запустить.
package main

// Импортируем пакеты, используемые в данном файле.
//
// Документацию по стандартным и сторонним пакетам легко найти по адресу
// https://godoc.org/<package_path>.
// Например,
//
//   * https://godoc.org/flag
//   * https://godoc.org/github.com/valyala/fasthttp
import (
	"flag"
	"github.com/valyala/fasthttp"
	"log"
)

// Объявляем глобальную переменную addr, куда будет записано значение параметра
// -addr при запуске программы.
//
// Например, параметр addr станет равным ":80" для следующей строки
// запуска:
//	./server -addr=:80
//
// Пропущенный IP в TCP адресе говорит о том, чтобы сервер "слушал"
// на всех доступных IP-адресах.
//
// flag.String указывает на то, что значение -addr - строка.
// flag.String принимает три аргумента:
//
//   * Название аргумента, который нужно распарсить. "addr" в данном случае.
//   * Значение аргумента по умолчанию. "127.0.0.1:8080" в данном случае.
//   * Описание аргумента, которое выводится при вызове программы
//     с параметром -help.
//
// flag.String возвращает указатель на строку, где хранится значение -addr.
var addr = flag.String("addr", "127.0.0.1:8080",
	"TCP address to listen to for incoming connections")

// main - функция, с которой начинается выполнение программы.
// Эта функцию должна находиться в package main.
func main() {
	// Парсим параметры, указанные в строке запуска программы.
	flag.Parse()

	// Конфигурируем http сервер.
	//
	// См. возможные параметры конфигурации
	// в https://godoc.org/github.com/valyala/fasthttp#Server
	s := fasthttp.Server{
		// Hanlder - функция-обработчик входящих http запросов.
		// См. код функции handler ниже.
		Handler: handler,
	}

	// Запускаем сервер.
	//
	// ListenAndServe принимает TCP адрес, где будет запущен сервер.
	// ListenAndServe возвращает результат только в двух случаях:
	//
	//   * Если во во время запуска сервера произошла ошибка.
	//     Например, указанный адрес уже занят другим сервером.
	//     Тогда соответствующая ошибка попадет в err.
	//   * Если сервер был остановлен. Тогда err будет равно nil.
	err := s.ListenAndServe(*addr)
	if err != nil {
		log.Fatalf("error in ListenAndServe: %s", err)
	}
}

// handler обрабатывает входящие запросы.
func handler(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Hello, world!\n")
}

Этот файл использует сторонний пакет — github.com/valyala/fasthttp , который нужно установить перед компиляцией. Сделаем это:

$ go get -u github.com/valyala/fasthttp

Исходники всех сторонних пакетов, полученные с помощью go get, сохраняются в папку $GOPATH/src/. Про $GOPATH можно почитать в официальной документации.

Теперь скомпилируем наш веб-сервер:

$ go build ./server.go

В текущем каталоге должен появиться исполняемый файл с именем server. Убедимся в этом:

$ ls -l | grep server
-rwxrwxr-x  1 aliaksandr aliaksandr   6140200 May  7 19:39 server
-rw-rw-r--  1 aliaksandr aliaksandr      4020 May  7 19:31 server.go

Проверим, какие параметры он принимает:

$ ./server -help
Usage of ./server:
  -addr string
    	TCP address to listen to for incoming connections (default "127.0.0.1:8080")

Запустим его:

$ ./server

В отдельном окне убедимся с помощью nc, что сервер работает:

$ nc 127.0.0.1 8080
GET / HTTP/1.0

HTTP/1.1 200 OK
Server: fasthttp
Date: Sun, 07 May 2017 17:43:40 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 14
Connection: close

Hello, world!

Также можно открыть 127.0.0.1:8080 в браузере и убедиться, что сервер работает.

Проверим скорость его работы с помощью wrk.
Через одно подключение:

$ wrk -t 1 -c 1 http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    13.25us   24.26us   1.70ms   99.74%
    Req/Sec    76.71k     4.43k   81.43k    91.09%
  771609 requests in 10.10s, 109.64MB read
Requests/sec:  76399.78
Transfer/sec:     10.86MB

Через 1000 одновременных подключений:

$ wrk -t 2 -c 1000 http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.24ms    3.44ms 150.36ms   89.22%
    Req/Sec    82.85k    11.32k  110.93k    68.18%
  1643377 requests in 10.01s, 233.52MB read
Requests/sec: 164183.83
Transfer/sec:     23.33MB

Через 100 одновременных подключений, в каждом по 32 pipelined запроса:

$ wrk -t 2 -c 100 -s pipeline.lua http://127.0.0.1:8080 -- / 32
Running 10s test @ http://127.0.0.1:8080
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.40ms    1.46ms  39.22ms   90.68%
    Req/Sec   824.20k    75.28k    0.99M    78.50%
  16469856 requests in 10.05s, 2.29GB read
Requests/sec: 1638496.88
Transfer/sec:    232.83MB

Как видите, простой веб-сервер из пары десятков строчек на Go не требует ни nginx, ни Apache, и может обрабатывать более 1,6 млн запросов в секунду на обычном ноуте трехлетней давности.

Добавляем поддержку https

В стандартную поставку Go входит пакет crypto/tls, с помощью которого можно настраивать https на любой вкус и цвет. За разработку данного пакета отвечает Adam Langley, автор BoringSSL. Несколько фактов про crypto/tls:

Если у вас уже есть TLS-сертификат и вы хотите побыстрее включить поддержку https, то просто замените следующую строку в server.go:

err := s.ListenAndServe(*addr)

На

err := s.ListenAndServeTLS(*addr, certFile, keyFile)

Где certFile и keyFile — пути к файлам сертификата и соответствующего ключа.

Если нужна дополнительная настройка https, например, как описано в статье Exposing Go on the Internet, то нужно немного повозиться:

package main

import (
	"crypto/tls"
	"flag"
	"github.com/valyala/fasthttp"
	"log"
	"net"
)

var (
	addr        = flag.String("addr", "127.0.0.1:8080", "TCP address to listen to for http")
	tlsAddr     = flag.String("tlsAddr", "", "TCP address to listen to for https")
	tlsCertFile = flag.String("tlsCertFile", "", "Path to TLS certificate file")
	tlsKeyFile  = flag.String("tlsKeyFile", "", "Path to TLS key file")
)

func main() {
	flag.Parse()

	// Пытаемся запустить https сервер
	startTLS()

	// Запускаем http сервер
	log.Printf("Serving http on -addr=%q", *addr)
	err := fasthttp.ListenAndServe(*addr, handler)
	if err != nil {
		log.Fatalf("error in ListenAndServe: %s", err)
	}
}


func startTLS() {
	if len(*tlsAddr) == 0 {
		log.Printf("-tlsAddr is empty, so skip serving https")
		return
	}

	// Читаем TLS сертификат из файла
	cert, err := tls.LoadX509KeyPair(*tlsCertFile, *tlsKeyFile)
	if err != nil {
		log.Fatalf("cannot load cert for -tlsCertFile=%q, -tlsKeyFile=%q: %s",
		*tlsCertFile, *tlsKeyFile, err)
	}

	// Создаем net.Listener'а, который принимает подключения по -tlsAddr.
	ln, err := net.Listen("tcp4", *tlsAddr)
	if err != nil {
		log.Fatalf("cannot listen for -tlsAddr=%q: %s", *tlsAddr, err)
	}

	// Создаем требуемую конфигурацию tls.
	// См. https://blog.gopheracademy.com/advent-2016/exposing-go-on-the-internet/ .
	tlsConfig := tls.Config{
		PreferServerCipherSuites: true,
		CurvePreferences: []tls.CurveID{
			tls.CurveP256,
			tls.X25519,
		},
		MinVersion: tls.VersionTLS12,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		},
		Certificates: []tls.Certificate{cert},
	}


	// Создаем net.Listener'а для tls подключений поверх созданного
	// выше net.Listener'а
	tlsLn := tls.NewListener(ln, &tlsConfig)

	// запускаем https сервер в отдельном потоке
	log.Printf("Serving https on -tlsAddr=%q", *tlsAddr)
	go fasthttp.Serve(tlsLn, handler)
}

func handler(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Hello, world!\n")
}

Теперь при указании параметров -tlsAddr, -tlsCertFile и -tlsKeyFile сервер будет принимать https-запросы на -tlsAddr дополнительно к http-запросам на -addr. И снова никаких nginx’ов c apache’ами и openssl’ами не нужно. Скорость обработки https-трафика сервером на Go сравнима со скоростью nginx, поэтому перед ним не нужно ставить TLS termination proxy.

Автоматизируем бесплатное получение и обновление TLS-сертификатов

Многие уже слышали про прекрасный сервис letsencrypt.org, который выдает всем желающим бесплатные TLS-сертификаты. И эти сертификаты признаются всеми современными браузерами. Ниже показано, насколько просто добавить поддержку автоматического получения и обновления TLS-сертификатов letsencrypt.org в наш сервер на Go:

package main

import (
	"crypto/tls"
	"flag"
	"github.com/valyala/fasthttp"
	"golang.org/x/crypto/acme/autocert"
	"log"
	"net"
)

var (
	addr        = flag.String("addr", "127.0.0.1:8080", "TCP address to listen to for http")
	tlsAddr     = flag.String("tlsAddr", "", "TCP address to listen to for https")
	tlsCertFile = flag.String("tlsCertFile", "", "Path to TLS certificate file. "+
		"The certificate is automatically generated and put "+
		"to -autocertCacheDir if empty")
	tlsKeyFile = flag.String("tlsKeyFile", "", "Path to TLS key file. "+
		"The key is automatically generated and put "+
		"to -autocertCacheDir if empty")
	autocertCacheDir = flag.String("autocertCacheDir", "autocert-cache",
		"Path to the directory where letsencrypt certs are cached")
)

func main() {
	flag.Parse()

	// Пытаемся запустить https сервер
	startTLS()

	// Запускаем http сервер
	log.Printf("Serving http on -addr=%q", *addr)
	err := fasthttp.ListenAndServe(*addr, handler)
	if err != nil {
		log.Fatalf("error in ListenAndServe: %s", err)
	}
}

func startTLS() {
	if len(*tlsAddr) == 0 {
		log.Printf("-tlsAddr is empty, so skip serving https")
		return
	}

	// Создаем net.Listener'а, который принимает подключения по -tlsAddr.
	ln, err := net.Listen("tcp4", *tlsAddr)
	if err != nil {
		log.Fatalf("cannot listen for -tlsAddr=%q: %s", *tlsAddr, err)
	}

	// Создаем требуемую конфигурацию tls.
	// См. https://blog.gopheracademy.com/advent-2016/exposing-go-on-the-internet/ .
	tlsConfig := tls.Config{
		PreferServerCipherSuites: true,
		CurvePreferences: []tls.CurveID{
			tls.CurveP256,
			tls.X25519,
		},
	}

	if len(*tlsCertFile) > 0 {
		// Читаем TLS сертификат из файла
		cert, err := tls.LoadX509KeyPair(*tlsCertFile, *tlsKeyFile)
		if err != nil {
			log.Fatalf("cannot load cert for -tlsCertFile=%q, -tlsKeyFile=%q: %s", *tlsCertFile, *tlsKeyFile, err)
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	} else {
		// Настраиваем автоматическое создание и обновление сертификатов.
		m := autocert.Manager{
			Prompt: autocert.AcceptTOS,

			// Сертификаты будут кэшироваться в -autocertCacheDir,
			// чтобы при рестарте сервера не приходилось
			// пересоздавать их снова.
			Cache: autocert.DirCache(*autocertCacheDir),
		}
		tlsConfig.GetCertificate = m.GetCertificate
	}

	// Создаем net.Listener'а для tls подключений поверх созданного
	// выше net.Listener'а
	tlsLn := tls.NewListener(ln, &tlsConfig)

	// запускаем https сервер в отдельном потоке
	log.Printf("Serving https on -tlsAddr=%q", *tlsAddr)
	go fasthttp.Serve(tlsLn, handler)
}

func handler(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Hello, world!\n")
}

Перед компиляцией данного кода понадобится скачать еще один сторонний пакет — golang.org/x/crypto/acme/autocert, который отвечает за автоматическое создание и обновление TLS-сертификатов:

$ go get -u golang.org/x/crypto/acme/autocert

Теперь сервер будет автоматически создавать и обновлять TLS-сертификаты для всех hostname’ов, запрошенных по https адресу -tlsAddr, если не указан -tlsCertFile. Выписанные сертификаты будут кэшироваться в каталоге -autocertCacheDir.

Заключение

Как вы могли убедиться, на Go можно легко и непринужденно создавать самодостаточные высокопроизводительные http- и https-серверы, которым не нужны никакие зависимости, включая Apache, nginx и OpenSSL. Код получается лаконичным и простым, без лишних абстракций и xml-конфигов.

В статье рассмотрен простейший http-сервер, выдающий «Hello, world!». На Go можно создавать сервера и прокси с намного более сложной логикой. В качестве примера рекомендую оценить простой в использовании http-прокси, балансировщик нагрузки и TLS termination прокси, который также умеет экономить трафик — httptp.

Предлагайте в комментариях темы по Go для следующих статей.

69 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Молодец(нет), автор написал как написать свой web сервер при помощи сторонней библиотеки. Наблюдательные могли заметить, что данная библиотека была написана тоже автором статьи. Отличная реклама(нет). И го порекламил, и свою либу.

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

Я не спорю, Вы правы. Но есть net/http из коробки, которой для любого hello world хватит за глаза. Согласны? Здесь же мы видим почему-то статью от автора с туториалом как написать сервер используя его же либу. Напрашивается вопрос, это так автор решил людей с го познакомить( странно что не путем знакомства с его возможностями с коробки) или решил попиарить свою библиотеку...

Именно! Решил раскрутить свою неизвестную библиотеку за счет читателей доу!

У неизвестной библиотеки 4к звезд на гитхабе, не думаю что ему это нужно.

Похоже, у dou большая аудитория читателей — столько звезд наставили после публикации этой статьи :)

По поводу зачем нжинкс — для хелло ворлд он конечно ни за чем, но если вы будете реализовывать пулл из серверов к примеру, то уже встаёт вопрос в адекватности такого подхода. Ну и про кеширование и прочее я не говорю конечно

Несомненно, nginx хорош в указанных вами областях. Но бывают ситуации, когда стандартной функциональности nginx не хватает, и стоит выбор:
— создать собственный модуль nginx для решения возникшей задачи;
— притащить какой-нибудь openresty и написать код на урезанном lua или js без нормальной поддержки shared memory и блокирующих сетевых операций;
— либо по-быстрому реализовать требуемую функциональность на go, не потеряв в качестве кода, производительности и возможностях.

Вот для таких случаев go — илеальный выбор. См., например, httpd — github.com/…​rt/tree/master/cmd/httptp — простой, удобный в использовании и расширении прокси-сервер на go, покрывающий основные «фишки» nginx.

Пока нельзя будет тремя строчками кода (как на ноде), не взлетит

Ну, у вас может и не взлетит, а почему-то у гугла, докера и многих других компаний уже давно в полёте.

Вообще, иногда забавляют вот такие вот комментарии «экспертов отрасли» о будущем ЯП.

Я уже молчу о том, чтобы почитать комментарии внизу и увидеть, что в std либе это действительно делается парой строчек.

Думаю он не троллит, а просто имеет место юношеский максимализм.

В три строчки не получится, но можно уложиться в пять строк кода — dou.ua/…​icles/go-is-easy/#1111051 . Выходит, go до nodejs еще не дорос ;(

Проблема не в кол-ве строчек, а в том, что го не может в структурную обработку ошибок, что делает его неюзабельным чуть более чем везде

Расскажите подробнее про структурную обработку ошибок, а также как она улучшает юзабельность языка программирования.

Там много текста, в котором сложно найти преимущества перед go. Хотелось бы краткого и ясного ответа вашими словами.

Слишком сложно. Должно быть так же просто как на яве:

import org.rapidoid.setup.On;

public class Main {

     public static void main(String[] args) {
         On.get("/size").json((String msg) -> msg.length());
     }

}

http://localhost:8888/size?msg=abc

Кста, этот серверок на 30% быстрее гошечки :) - www.techempower.com/…​-r14&hw=ph&test=plaintext
Сервак, кому интересно — www.rapidoid.org

В кнопочке «filter panel» я имел виду. По ссылке просто попадаешь на вкладку. Сложно понять в сравнении с чем отличие в 30% потому как решений с Go там много и с некоторым разница куда больше чем на треть.

Сложно понять в сравнении с чем отличие

С этим — fasthttp-mysql-prefo

Это сервер из статьи, если я правильно понял

github.com/valyala/fasthttp
Кста, этот серверок на 30% быстрее гошечки :) - www.techempower.com/...​-r14&hw=ph&test=plaintext

Если переключиться на вкладку Data table, то можно увидеть, что rapidoid выигрывает у fasthttp только при 256 одновременных подключений. При 1024 подключений и выше гошечка уделывает rapidoid. Интересно, сколько этот rapidoid займет памяти и как будет тормозить при сотнях тысяч одновременных подключений — стандартном режиме работы fasthttp у нас в продакшн.

при 16К подключений ноздря в ноздрю.

При 16к подключений начинает тормозить load testing tool — wrk, так что результаты тестов становятся недостоверными. См. комментарий от автора rapidoid за 30 марта вот тут — groups.google.com/…​rk-benchmarks/sQDY1uELRkY .

Ок, но замеры latency тоже любопытны

С увеличением количества одновременных подключений растет latency. Причем не только из-за сервера, но и из-за wrk. Для rapidoid указана latency при 256 одновременных подключения, а для fasthttp — при намного большем количестве подключений. Так что сравнивать latency в данном бенчмарке не совсем корректно.

Забавно, есть бенчмарки, но кривые. Хотя, на мой взгляд, сама идея меряться hello world приводит к сугубо попугайным результатам.

Забавно, есть бенчмарки, но кривые.

Именно так. Например, бенчмарк под нетти имеет 2 лишних аллокации и лишний вызов метода в простейшем «hello world». Поэтому, например Undertow операжет нетти, но мы то знаем :).

Отправьте pull request с фиксом — в следующем раунде netty опередит undertow. Вот их репозиторий — github.com/…​power/FrameworkBenchmarks .

нтересно, сколько этот rapidoid займет памяти и как будет тормозить при сотнях тысяч одновременных подключений — стандартном режиме работы fasthttp у нас в продакшн.

Да я думаю все там нормально будет. Какой транспорт fasthttp юзает?

Не совсем понял вопроса, но попробую ответить. В проде к серваку на fasthttp подключаются клиенты по tcp и шлют http/1.1 запросы в режиме http keepalive.

Я имел в виду — какиe системные вызовы fasthtpp использует в своей реализации — poll/epoll/select? Я так понимаю, что epoll?

В fasthttp используются стандартный механизм go для сетевых подключений -
net.Conn. Что там под капотом, мне, как разработчику fasthttp, не очень интересно. Главное, чтобы net.Conn было удобно использовать (никакого асинхронного кода со state machine’ами на калбэках) и чтобы он быстро работал.

strace под линукс показывает epoll. Предполагаю, что под другими поддерживаемыми операционными системами используются другие механизмы.

strace под линукс показывает epoll.

Вот... Я это к тому, что даже на уровне операционки это могут быть разные транспорты. Джавоский nio, например, может юзать епол, но эта реализация из коробки не оптимальная и на каждый select() аллоцируется hashmap, итерация по hashmap аллоцирует iterator и тд. Хотя вот именно рапидоид использует nio и довольно быстрый.

Слишком сложно. Должно быть так же просто как на яве:
Уберите комментарии из кода в статье — поле этого сравнивайте. Если убрать парсинг параметров и обработку ошибок, которые отсутствуют в вашем коде, то получится 7 строчек кода, который намного понятнее вашего кода:
package main
import "github.com/valyala/fasthttp"
func main() {
    fasthttp.ListenAndServe(":8080", func(ctx *fasthttp.RequrstCtx) {
        ctx.WriteString("Hello, World!")
    })
}

Несколько вопросов по вашему коду:
— Где указан порт, на котором будет работать rapidoid?
— Как запустить rapidoid на нескольких портах?
— Как запустить разные обработчики запросов на разных портах? Например, на 80 порту — application server, а на порту 12345 — сервер с технической информацией по состоянию приложения.

— Где указан порт, на котором будет работать rapidoid?

Там дефолтный вшит для простоты.

— Как запустить rapidoid на нескольких портах?

Я не эксперт по рапидоиду, просто скинул как простейший пример. Думаю так же как во всех других аналогичных продуктах. Например, в нетти я делаю так bind(8080).bind(8081);

— Как запустить разные обработчики запросов на разных портах? Например, на 80 порту — application server, а на порту 12345 — сервер с технической информацией по состоянию приложения.

bind(8080, handlers1).bind(8081, handlers2);

Дякую, маю надію, що це лише початок.

Ще б дайджест, та й на «беларускай мове» :)

також за дайджест, можна українською, Oleksandr Manenko перекладе на бєларуську ;)
дякую за статтю

Це було б важко :) Читати можу, писати — ні.

зачем эта нубская статья тут? даже на хабре такое бы не зашло

Ага, не хватает статей про теорию категорий, монады, аппликативные функторы, паттерн матчинг и scala :)

Надеюсь доу не скатится и сюда все еще можно будет зайти и отдохнуть от полотен кода

монады, аппликативные функторы
Монада есть аппликативный функтор :Р

Техническая статья на ДОУ. Вах.

По сабжу. Ваш пакет fasthttp, безусловно, все в сообществе знают и уважают, но, на мой взгляд, лучше всё-таки использовать его только тогда, когда это действительно нужно, тем более для обучающих статей. Не только по причине отсутствия в нём http/2, но и потому, что std’шный net/http является классным примером для применения многих классных штук в Go, вроде HandlerFunc. Я же молчу про несметное кол-во библиотек и примеров для стандартной сигнатуры handler’а (w http.ResponseWriter, r *http.Request).

Согласен.

fasthttp здесь использовался по нескольким причинам:
— Чтобы заинтересовать читателей скоростью обработки запросов в 1.6 миллиона запросов в секунду. С net/http вышло бы 100к qps, что не настолько впечатляет :)
— Чтобы упростить код для начинающих программистов. net/http требует создавать отдельный объект с методом ServeHTTP либо заворачивать единственную функцию-обработчик запросов в HandlerFunc. Эта функция принимает два аргумента вместо одного, как указано в вашем комментарии, что дополнительно усложняет код.
— Чтобы прорекламировать fasthttp :)

А если разнести сервер и нагрузочную тулу по разным машинам?

Попробуйте — в статье приведен полностью рабочий код, чтобы читатели могли начать экспериментировать с go и http сервером.

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

Невже на DOU технічна стаття? Я не сплю?
А якщо без гумору, дякую за статтю. Коли чекати наступну з циклу?

Скоро будет продолжение

Добрый день!
Спасибо за статью!
Не могли бы Вы рассказать о расширении коммерческого проекта, написанного на C, модулем на Go? Понятное дело, части, написанные на C и Go должны как-то между собой взаимодействовать. Интересует, прежде всего, какие есть эффективные способы построения коммуникации между C- и Go-кодом, какие в таком случае могут быть подводные камни.
Спасибо!

Старый добрый pipe никто не отменял, как самый простой вариант

Самый простой способ — использовать какой-нибудь RPC протокол (для небольшой нагрузки отлично подойдет http-based rpc типа REST или json-rpc) для взаимодействия между сервисами, написанными на разных языках программирования.

Способ посложнее — использовать cgo — это встроенная в go поддержка для взаимодействия с библиотеками, написанными на C. См. golang.org/cmd/cgo .

Есть также возможность собирать из go-кода статические и динамические библиотеки, которые затем можно использовать из программ на C. См. golang.org/…​escription_of_build_modes

Go — это просто.
HelloWorld
пару страниц текста и кода.
Нет, спасибо

Так понятней?

package main

import (
	"io"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Hello world!")
}

func main() {
	http.HandleFunc("/", hello)
	http.ListenAndServe(":8000", nil)
}

Здесь используется не стдшный пакет для http (fasthttp, и пусть вас не запутывает название, он не всегда и не везде быстрее стандартного) и кастомно настраивается TLS.
Не лучший пример для новичка и уж тем более для HelloWorld.

Цель статьи — показать читателям, мало знакомым с go, насколько просто создать высокопроизводительный, готовый к применению в продакшн http-сервер на go. Если бы использовался net/http вместо fasthttp, то результаты тестирования производительности были бы не столь впечатляющими.

Разделы про настройку tls и letsencrypt должны показать, насколько просто добавить высокопроизводительную поддержку последних нововведений https с автоматической выпиской сертификатов, готовую для использования в продакшн.

По замыслу, читатели должны были сравнить геморрой по корректной настройке https аналогичной функциональности в других решениях (nginx, apache, java, nodejs) с полностью готовым к использованию кодом из статьи, и сделать очевидный выбор в пользу go :)

читатели должны были сравнить геморрой по корректной настройке https аналогичной функциональности в других решениях (nginx, apache, java, nodejs)

В нетти я заимплементил это за 2 часа. Делаю так :

java -jar server.jar -host test.blynk.cc

и на выходе получаю сертификаты на этот хост + реньювал по крону внтри джарки. Видел явовские сервера, которые такое из коробки уже поддерживают.

80% текста занимают комментарии для новичков. Уберите их, чтобы узнать, сколько там на самом деле кода

В какой opensource проект на Go посоветуешь законтрибьютить?

github.com/go-kit/kit — микросервисы, нужны генераторы scaffold’а сервисов (обертки для разных транспортов и тд), т.к. вручную это долго писать. Средний порог входа.

github.com/go-gitea/gitea — личный github, нужны улучшения по LDAP и в целом. Местами говнокод, т.к. форк проекта от китайцев, приводят всё в порядок.

github.com/therecipe/qt — нативные биндинги к Qt на всех платформах. Нужна помощь с документацией и тестированием. Высокий порог входа + знание Qt.

Можно (и нужно) в целом смотреть сюда — github.com/avelino/awesome-go — чтобы узнать что у сообщества уже есть крутое.

В go — golang.org/doc/contribute.html . И это не шутка — код самого go (компилятор, линкер, рантайм, стандартная библиотека, вспомогательные инструменты) написан в основном на go, поэтому его легко читать и рефакторить.

Аляксандр, а навошта ты дадаў fasthttp, а не паказаў натыўны код? У прынцыпе я разумею што сваё радней да цела або быў намысел?

Пытаешься переключить народ с яндекс-срача на свеженький go-срач?
Одобряю))

Подписаться на комментарии