我一直在尝试使用 nginx 作为我的外部 REST GET API 之一的反向代理来调试我的 golang 服务和外部服务之间的延迟问题。
我可以轻松地通过golang的net/http库调用该公共URL并获得成功的响应,但是当我使用nginx作为反向代理时,nginx给出了502 Bad Gateway。在 nginx-error.log 文件中,我收到有关 SSL 的错误,如下所示,但想知道如何在通过 golang net/http 客户端调用该 API 时不会收到错误。
P.S:我通过创建一个简单的 /ping-nginx API 来交叉检查 nginx 是否正在运行,该 API 仅返回 200 statusCode 和 pong-nginx 作为响应。
ping.go
package main
import (
"fmt"
"log"
"net/http"
"strconv"
)
func main() {
http.HandleFunc("/ping-via-go-client", func(w http.ResponseWriter, r *http.Request) {
url := "https://flights-explorer.makemytrip.com/ping"
resp, err := http.Get(url)
var statusCode string
if err == nil {
statusCode = strconv.Itoa(resp.StatusCode)
}
fmt.Fprintf(w, "pong-via-go-client with statusCode:"+statusCode)
})
http.HandleFunc("/ping-via-nginx", func(w http.ResponseWriter, r *http.Request) {
url := "http://localhost/ping"
resp, err := http.Get(url)
var statusCode string
if err == nil {
statusCode = strconv.Itoa(resp.StatusCode)
}
fmt.Fprintf(w, "pong-via-nginx with statusCode:"+statusCode)
})
log.Fatal(http.ListenAndServe(":3003", nil))
}
卷发的反应:
➜ ~ curl http://localhost:3003/ping-via-go-client
pong-via-go-client with statusCode:200%
➜ ~ curl http://localhost:3003/ping-via-nginx
pong-via-nginx with statusCode:502%
nginx.conf
#user nginx;
daemon off;
worker_processes auto;
error_log /opt/logs/nginx-error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 250;
}
http {
log_format upstream_time '$time_local $status $remote_addr to:- $upstream_addr $request '
'uct:$upstream_connect_time uht:$upstream_header_time urt:$upstream_response_time '
'request_time:$request_time tid_header:$http_tid status:$upstream_cache_status '
'slot:$http_slot slot_time:$http_slotstarttime ttl_req:$http_ttl ttl_resp:$upstream_http_x_accel_expires '
'job_flag:$http_jobflag cookies:"$http_cookie" bytes_sent:$bytes_sent gzip_ratio:$gzip_ratio '
'"$http_referer" "$http_user_agent" $http_x_forwarded_for cur_time:$msec';
keepalive_timeout 85;
upstream flights-explorer.makemytrip.com {
server flights-explorer.makemytrip.com:443;
keepalive 30;
}
server {
listen 80;
access_log /opt/logs/nginx-access.log upstream_time;
location = /basic_status {
stub_status;
}
location /ping-nginx {
return 200 'pong-nginx\n';
add_header Content-Type text/plain;
}
location /ping {
# proxy_buffering off;
proxy_pass https://flights-explorer.makemytrip.com$request_uri;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_ssl_verify off; # Disable SSL certificate verification
proxy_ssl_verify_depth 0;
#proxy_ssl_session_reuse on;
#proxy_socket_keepalive on;
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
}
}
}
nginx-error.log 中的错误
2024/03/03 11:07:55 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.25:443/ping", host: "localhost"
2024/03/03 11:07:55 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.25:443/ping", host: "localhost"
2024/03/03 11:07:55 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.67:443/ping", host: "localhost"
2024/03/03 11:07:55 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.67:443/ping", host: "localhost"
2024/03/03 11:07:56 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.74:443/ping", host: "localhost"
2024/03/03 11:07:56 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.74:443/ping", host: "localhost"
日志位于 nginx-access.log
03/Mar/2024:11:07:56 +0530 502 127.0.0.1 to:- 23.63.110.25:443, 23.63.110.67:443, 23.63.110.74:443 GET /ping HTTP/1.1 uct:-, -, - uht:-, -, - urt:0.131, 0.116, 0.107 request_time:0.354 tid_header:- status:- slot:- slot_time:- ttl_req:- ttl_resp:- job_flag:- cookies:"-" bytes_sent:314 gzip_ratio:- "-" "Go-http-client/1.1" - cur_time:1709444276.083
从评论中,你可以尝试:
http {
upstream backend {
server flights-explorer.makemytrip.com:443;
keepalive 30;
}
server {
listen 80;
location /ping {
proxy_pass https://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_set_header Host $host;
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_ssl_name $host;
}
location /ping-nginx {
return 200 'pong-nginx\n';
add_header Content-Type text/plain;
}
}
}
proxy_ssl_server_name on;
指令启用SNI支持,允许Nginx向上游服务器提供正确的虚拟主机。这反映了 Go http.Client
默认情况下的操作。
proxy_set_header Host $host;
确保传递到上游的 Host 标头与原始请求匹配,上游服务器可能需要这样做才能正确处理请求。
proxy_ssl_verify off;
仅对调试有用。出于安全目的,您必须在生产环境中启用它:它会绕过 SSL 证书验证,类似于如果未明确强制执行证书验证,Go 客户端中可能会发生的情况。
尽管评论提到 API 应该以 <1ms, network conditions or server processing times might still cause delays. Adjusting
proxy_connect_timeout
和 proxy_read_timeout
适当地响应,但再次可以为调试目的提供帮助。error_log /path/to/error.log debug;
增强 Nginx 的日志记录以获得更详细的调试信息。
Go
http.Client
自动处理 HTTP 和 SSL 通信的许多方面,包括 SNI 和主机标头。默认 Go 客户端对 SSL、标头或 keep-alives
的处理方式可能与上游服务器的期望更兼容。tcpdump
等网络嗅探工具来捕获从 Go 客户端到服务器的流量)和 Nginx(通过调试日志)生成的实际请求标头和 SSL 握手详细信息,以发现差异。