Pull to refresh

Comments 13

Получается, что например, если у меня две сетевых карты, каждая подключена к интернету через своего провайдера и я запущу такой сервер на Go, то все запросы на любой сетевой интерфейс будут идти в этот Go-сервис и ответ этот сервис будет слать через любой из двух сетевых интерфейсов выбрав по определенному алгоритму? Или в чем смысл такого сервера?

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

Всё так, мультиплексирование каналов возможно с обеих точек- и как с клиента, и так и с сервера. Основное требование- поддержка на уровне ядра ОС данного протокола.

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

Можно включить MPTCP без пересборки приложения, используя переменные окружения.

% GODEBUG=multipathtcp=1 go run mptcp.go &
[1] 70247
% mptcpize run curl 'http://127.0.0.1:8080/'
conn.MultipathTCP() = (true, <nil>)
Исходник сервера
package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
)

type ctxKey uint8

const(
	ctxHttpConnKey = ctxKey(iota)
)

func ctxHttpSaveConn(ctx context.Context, conn net.Conn) context.Context {
	return context.WithValue(ctx, ctxHttpConnKey, conn)
}

func ctxHttpLoadConn(ctx context.Context) *net.TCPConn {
	return ctx.Value(ctxHttpConnKey).(*net.TCPConn)
}

func main() {
	serv := &http.Server{
		Addr: ":8080",
		ConnContext: ctxHttpSaveConn,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			conn := ctxHttpLoadConn(r.Context())
			mptcp, err := conn.MultipathTCP()
			fmt.Fprintf(w, "conn.MultipathTCP() = (%t, %v)", mptcp, err)
		}),
	}

	log.Fatal(serv.ListenAndServe())
}

Скажите, а почему выбрали Go для такой задачи? Не мешает ли его GC периодическими просадками?

Здравствуйте, потому что:

  • люблю писать на Go

  • статья о добавлении в стандартную библиотеку функционала MPTCP, в бою не пробовал.

При этом, на мой дилетантский взгляд, GC может помешать только если нужна потребность в обработке данных в режиме реального времени (типа систем реального времени), в остальном GC никому не мешает, а даже помогает.

Удивляет меня несколько этот вопрос. "для такой задачи" это какой?)
Просто похоже, вас сильно пугает наличие gc как факт, хоть ведь та же java (на которой, например, написана весь производительная kafka) - тоже использует gc.
Ну и в целом Go был создан гуглом на замену C++ для написания сетевых сервисов. И сеть - это прям конёк гошки, она почти идеальная для написания бэкенда.

Скажите, как вы определили что меня сильно пугает сборщик мусора?

Я на языках с GC пишу около 20 лет. Но для вещей, где вся логика - это работа с сетевыми пакетами предпочитаю все таки языки без GC.

Потому что вы спросили про GC, который в нормальной ситуации в GO работает очень редко и паузит приложение примерно никогда.

Он не может работать очень редко. Он работает очень часто, но в отдельном потоке и работает на lock-free структурах. Но временами все такие приводит к stop the world.

Я специально перечитал, мало ли я тупой и вы правы, но вы не правы.
https://tip.golang.org/doc/gc-guide

It works by determining the target heap size after each GC cycle, a target value for the total heap size in the next cycle. The GC's goal is to finish a collection cycle before the total heap size exceeds the target heap size. Total heap size is defined as the live heap size at the end of the previous cycle, plus any new heap memory allocated by the application since the previous cycle.
...
The heap target controls GC frequency: the bigger the target, the longer the GC can wait to start another mark phase and vice versa. While the precise formula is useful for making estimates, it's best to think of GOGC in terms of its fundamental purpose: a parameter that picks a point in the GC CPU and memory trade-off. The key takeaway is that doubling GOGC will double heap memory overheads and roughly halve GC CPU cost, and vice versa. (To see a full explanation as to why, see the appendix.)


Go довольно неплохо аллоцирует на "стеке" и в heap данные уходят не так часто.
То что заведомо будет часто аллоцироваться - привет memory pool. Хотите ручной как в ядре с готовыми структурами, хотите автоматический (sync.pool).

GC запускается только, когда память выросла по умолчанию в 2 раза от текущей.

это плохая аргументация "у меня есть" (вы можете сами попробовать и увидеть как это работает).
У меня есть довольно много сервисов на GO в продакшене, и по мониторингу памяти явно видно когда GC работает (т.к. он освобождает память обратно системе).

Если у вас приложение запустилось, допустим набрало памяти до 1GiB (в процессе GC отработал несколько раз, последний допустим на 1GiB). То следующий вызов GC будет только, если приложение распухнет до 2 гигов (GOGC=100). В рамках этих 2х гигобайт он отрабатывает никогда.

"без GC" мне на ум приходят разве что c/c++ и rust из более-менее популярных. Вы правда пишете любой бэкенд сервис на одном из вышеперечисленных языков? Очень необычный выбор. Мне кажется, что в этом сегменте довольно давно правят баллом Java и C#.

Ну и ещё стоит посмотреть в сторону популярных ныне NATS, Centrifugo, Traefik, Prometheus/VictoriaMetrics, Consul, Vault - уж куда более "сетевых" продуктов трудно найти. И все они на go.

Сейчас Rust использую и Java. Если речь про меньшую логику и большие требования к производительности - Rust.

Sign up to leave a comment.

Articles