4
votes

Spring Boot aléatoire "SSLException: Connection reset" dans Kubernetes avec JDK11

Le contexte:
  • Nous avons une application Web Spring Boot (2.3.1.RELEASE)
  • Il est écrit en Java 8 mais s'exécute à l'intérieur d'un conteneur avec Java 11 ( openjdk:11.0.6-jre-stretch ).
  • Il dispose d'une connexion DB et d'un service en amont qui est appelé via https (méthode d'échange simple RestTemplate #) (c'est important!)
  • Il est déployé à l'intérieur d'un cluster Kubernetes (je ne sais pas si cela est important)

Problème:

  • Chaque jour, je vois un petit pourcentage de demandes vers le service en amont échouer avec cette erreur: Erreur d' I/O error on GET request for "https://upstream.xyz/path": Connection reset; nested exception is javax.net.ssl.SSLException: Connection reset
  • Les erreurs sont totalement aléatoires et se produisent par intermittence.
  • Nous avons eu une erreur similaire ( javax.net.ssl.SSLProtocolException: Connection reset ) qui était liée à JRE11 et c'est un problème de négociation TLS 1.3. Nous avons mis à jour notre image Docker comme mentionné ci-dessus et cela l'a corrigé.
  • Voici la trace de la pile de l'erreur:
public static RestTemplate create(final int maxTotal, final int defaultMaxPerRoute,
                                  final int connectTimeout, final int readTimeout,
                                  final String userAgent) {
    final Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", SSLConnectionSocketFactory.getSocketFactory())
            .build();

    final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(schemeRegistry);
    connManager.setMaxTotal(maxTotal);
    connManager.setDefaultMaxPerRoute(defaultMaxPerRoute);

    final CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connManager)
            .setUserAgent(userAgent)
            .setDefaultRequestConfig(RequestConfig.custom()
                                             .setConnectTimeout(connectTimeout)
                                             .setSocketTimeout(readTimeout)
                                             .setExpectContinueEnabled(false).build())
            .build();

    return new RestTemplateBuilder()
            .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
            .build();
}

Configuration:

java.net.SocketException: Connection reset
    at java.base/java.net.SocketInputStream.read(Unknown Source)
    at java.base/java.net.SocketInputStream.read(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketInputRecord.read(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(Unknown Source)
    at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
    at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
    at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:87)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:739)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583)
....

Quelqu'un a eu ce problème? Lorsque j'active les journaux de débogage sur le client http, il déborde de bruit et je suis incapable de discerner quoi que ce soit d'utile ...


2 commentaires

Avez-vous un moyen de contacter les personnes qui gèrent le serveur en amont? Peut-être que le serveur en amont équilibre la charge de vos demandes entre un pool de serveurs, dont l'un est mal configuré. Ou peut-être que le serveur de l'autre côté a été redémarré.


En attendant, vous pouvez ajouter une logique de nouvelle tentative et voir si une deuxième et une troisième tentative échouent également.


3 Réponses :


0
votes

Je suppose que le problème est lié aux k8.

  1. si vous utilisez la flanelle comme réseau k8s, veuillez vérifier l'état de la flanelle et trouver si elle redémarre plus de fois. utiliser la commande ci-dessous
# to check linux kennel version
uname -sr 

# upgrade step
1)
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel -y install kernel-lt
2) open and edit /etc/default/grub, and set "GRUB_DEFAULT=0"
3) grub2-mkconfig -o /boot/grub2/grub.cfg
4) reboot
  1. quelle version de votre chenil linux? si ce n'est pas la version 4.x ou supérieure, veuillez mettre à niveau vers 4.x.
kubectl get pod -n kube-system | grep flannel

Je souhaite qu'il soit utile pour résoudre le problème.


0 commentaires

0
votes

Une trace de pile SSL comme celle-ci pourrait être causée par de nombreuses raisons différentes qui pourraient n'avoir rien à voir avec SSL lui-même. Ce stacktrace ne vous aidera pas assez, et de plus, ce problème n'a rien à voir avec spring, resttemplate, etc.

Ce qui vous aidera, c'est que si vous implémentez un cadre de journalisation / surveillance / traçage, j'utilise elasticsearch. Surveillez le comportement pendant quelques jours, assurez-vous d'enregistrer autant d'informations que nécessaire dans ces journaux, telles que l'ID du conteneur, les détails de la connexion (quand elle a été lancée, etc.). Vous constaterez peut-être que, par exemple, après qu'une connexion a vécu pendant un certain temps (par exemple 1 heure), cela se produit, et si vous établissez simplement des connexions en direct pendant moins de temps, le problème disparaîtra.

De cette façon, vous pourrez peut-être résoudre le problème sans avoir besoin de trouver la cause profonde, car cela pourrait prendre plusieurs jours de travail et vous mener nulle part. Plutôt bricoler les paramètres de connexion résoudra potentiellement votre problème. Mais pour cela, vous avez besoin de plus de visibilité car les informations que vous avez publiées jusqu'à présent ne sont pas suffisantes pour résoudre le problème.


0 commentaires

1
votes

Je vais partager mon expérience sur cette erreur probablement c'est le même problème que vous rencontrez. En comparant la trace de pile que j'avais.

Comme cela se produit au hasard, c'est la phrase clé que je soupçonne que c'est le même problème.

Les connexions HTTP sont établies via une bibliothèque client HTTP (Apache HTTP Client).

Le client HTTP gère généralement un pool de connexions réutilisable. Ce pool a une limite. Dans notre cas, le pool de connexions est parfois (au hasard ) totalement occupé. Il n'y a plus de connexions gratuites utilisables.

  1. Vous pouvez soit augmenter la taille de la piscine
  2. Implémentez un mécanisme de nouvelle tentative de back-off qui tentera de récupérer une connexion dans le pool de connexions HTTP en cas d'échec lors de l'exécution de la requête HTTP avec succès.

Si vous vous demandez comment régler ce client HTTP sous-jacent utilisé dans le démarrage du sprint, consultez cet article.


0 commentaires