Правильный способ использования HTTP/2 в Golang

Go HTTP

Golang сделал это относительно рано в http/2, но по историческим причинам API сбивает с толку, и многие онлайн-студенты жалуются.

Я запишу здесь, как правильно реализовать клиент и сервер HTTP/2.

Протокол HTTP/2

Рукопожатие протокола HTTP/2 делится на два способа: один называется h2, а другой — h2c.

h2 требует шифрования TLS.Во время рукопожатия TLS будет завершено согласование протокола HTTPS/2.Если согласование не удается (например, клиент не поддерживает его или сервер не поддерживает), HTTPS/1 будет используется для продолжения последующего общения.

h2c использует не TLS, а дополнительный цикл рукопожатия на основе протокола HTTP для завершения обновления до протокола HTTP/2, что обычно не рекомендуется.

Стандартная библиотека Go

http-библиотека GO по умолчанию поддерживает протокол HTTP/2, и пока мы используем TLS, функция HTTP/2 будет включена по умолчанию.

Библиотека http не поддерживает использование h2c при разработке API, но поощряет использование h2.

Пока мы используем TLS, библиотека http по умолчанию будет выполнять согласование HTTPS/2, а если согласование не удастся, она выродится в HTTPS/1.

Что смущает многих разработчиков, так это то, что когда мы хотим сделать некоторые дополнительные настройки для http-клиента или сервера, они переопределяют поведение библиотеки http по умолчанию, что приводит к невозможности включения протокола HTTP/2.

Ниже я расскажу, как убедиться, что протокол HTTP/2 включен.

Сервер

Мы явно должны использовать режим h2, что означает жертвование временем шифрования TLS, но в обмен на удобство HTTP/2.

Сделать сертификат

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

Сертификат самоподписан нами, а стороннего ЦС для проверки нет, поэтому клиенту необходимо отключить функцию проверки валидности сертификата.

1,生成服务端私钥
openssl genrsa -out default.key 2048
2,生成服务端证书
openssl req -new -x509 -key default.key -out default.pem -days 3650

default.key — закрытый ключ, а default.pem — сертификат.

свидетельство о проверке

Поскольку мы являемся сертификатом, подписанным в формате X509, поэтому программа готова сначала выполнить проверку валидности:

 // TLS证书解析验证
 if _, err = tls.LoadX509KeyPair(G_config.ServerPem, G_config.ServerKey); err != nil {
 return common.ERR_CERT_INVALID
 }

После подтверждения того, что сертификат действителен, мы, наконец, передаем сертификат и закрытый ключ через serverTLS, чтобы запустить службу HTTPS/2:

 // HTTP/2 TLS服务
 server = &http.Server{
 ReadTimeout: time.Duration(G_config.ServiceReadTimeout) * time.Millisecond,
 WriteTimeout: time.Duration(G_config.ServiceWriteTimeout) * time.Millisecond,
 Handler: mux,
 }
 
 // 监听端口
 if listener, err = net.Listen("tcp", ":" + strconv.Itoa(G_config.ServicePort)); err != nil {
 return
 }
 
 // 拉起服务
 go server.ServeTLS(listener, G_config.ServerPem, G_config.ServerKey)

Помимо использования ServeTLS для запуска сервера, поддерживающего функцию HTTPS/2, вы также можете использоватьhttp2.ConfigureServerчтобы включить функцию HTTPS/2 для http.Server и использовать Serve напрямую для запуска службы.

клиент

Самое главное для клиента — настроить транспорт, так называемый транспорт — это базовый диспетчер соединений, включая возможности обработки протокола.

Поскольку у нас много потребностей в настройке конфигурации клиента, мы сами сгенерировали транспорт вместо встроенного транспорта:

 transport = &http.Transport{
 TLSClientConfig: &tls.Config{InsecureSkipVerify: true,}, // 不校验服务端证书
 MaxIdleConns: G_config.GatewayMaxConnection,
 MaxIdleConnsPerHost: G_config.GatewayMaxConnection,
 IdleConnTimeout: time.Duration(G_config.GatewayIdleTimeout) * time.Second, // 连接空闲超时
 }

Конфигурация TLS объявляет InsecureSkipVerify=true, что означает, что ЦС не проверяет действительность сертификата.

Подобно серверу, поскольку мы не используем встроенный клиентский транспорт, нам нужно использоватьhttp2.ConfigureTransportЧтобы включить функцию HTTPS/2:

 // 启动HTTP/2协议
 http2.ConfigureTransport(transport)

Наконец, настройте транспорт к клиенту, который отвечает за базовое соединение и управление протоколом:

 // HTTP/2 客户端
 gateConn.client = &http.Client{
 Transport: transport,
 Timeout: time.Duration(G_config.GatewayTimeout) * time.Millisecond, // 请求超时
 }

Наконец

Сначала поймите разницу между h2 и h2c, а потом объясните, что язык Go рекомендует использовать h2.

Наконец, когда мы не используем конфигурацию http по умолчанию, нам нужно перенастроить, чтобы включить функцию HTTP2/S через http2.ConfigureXXX.