web-dev-qa-db-fra.com

Le thread RestTemplate est-il sûr?

Est-ce qu'un Spring RestTemplate est thread-safe? C'est

  • Est un RestTemplate un objet de stratégie que plusieurs connexions peuvent partager en toute sécurité. o
  • Est un RestTemplate un objet de connexion (comme une connexion à une base de données), qui ne peut pas être partagé pendant son utilisation, et nécessite une nouvelle création ou un regroupement pour chaque connexion.
57
Raedwald

RestTemplateest thread-safe (emphase ajoutée):

Conceptuellement, il est très similaire à JdbcTemplate, JmsTemplate et aux divers autres modèles trouvés dans Spring Framework et dans d'autres projets de portefeuille. Cela signifie, par exemple, que le RestTemplate est thread-safe une fois construit


Les objets de la classe RestTemplate ne modifient aucune de leurs informations d'état pour traiter HTTP: la classe est une instance du modèle de conception Strategy, plutôt que d'être comme un objet de connexion. Sans information d'état, il n'y a aucune possibilité que différents threads corrompent ou informations d'état de course s'ils partagent un objet RestTemplate. C'est pourquoi il est possible pour les threads de partager ces objets.

Si vous examinez le code source de RestTemplate vous verrez qu'il n'utilise pas les méthodes synchronized ou les champs volatile pour assurer la sécurité des threads après la construction de l'objet. Il est donc pas sûr de modifier un objet RestTemplate après la construction. En particulier, il n'est pas sûr d'ajouter un convertisseur de messages.

Pour lui fournir une liste de convertisseurs de messages, vous devez effectuer l'une des opérations suivantes:

  • Utilisez le constructeur RestTemplate(List<HttpMessageConverter<?>> messageConverters). Comme la liste interne de messageConverters est final, ceci publie en toute sécurité la liste des convertisseurs de messages .
  • Utilisez le mutateur setMessageConverters(List<HttpMessageConverter<?>> messageConverters) et puis publiez en toute sécurité l'objet RestTemplate modifié. Utiliser une définition de bean Spring qui a un <property name="messageConverters"><list>... Fait cela, car le bean sera publié en toute sécurité par le thread configurant le conteneur dans la plupart des cas d'utilisation pratiques.
  • Utilisez List.add Sur la référence renvoyée par getMessageConverters(), puis publiez en toute sécurité l'objet RestTemplate modifié. Cependant, la documentation de RestTemplate n'indique pas explicitement qu'elle renvoie une référence qui peut être utilisée pour modifier la liste des convertisseurs de messages. L'implémentation actuelle le fait, mais il est possible que l'implémentation soit modifiée pour renvoyer un Collections.unmodifiableList Ou une copie de la liste. Il serait donc préférable de ne pas le changer de cette façon.

Notez que le premier cas est le seul moyen de configurer les convertisseurs de messages lors de la construction de l'objet, donc il est correct de dire qu'il "est thread-safe une fois construit ".

La classe fait partie du Spring Framework, donc dans presque tous les cas pratiques, les objets de la classe seront configurés dans le cadre d'un contexte d'application Spring, en utilisant le premier (injection de dépendance à l'aide d'un constructeur) ou le second (injection de dépendance à l'aide d'un setter) et serait donc garanti d'être publié en toute sécurité sur plusieurs threads.

78
Raedwald

Il est thread-safe du point de vue de la bibliothèque. Par exemple, le getMessageConverters () est public, ce qui signifie que si quelqu'un s'accroche à la liste et la modifie en dehors du but de la bibliothèque, cela causera des problèmes (et même la méthode setter, si elle est appelée à tout moment après l'instanciation RestTemplate - et tout en étant utilisé par d'autres threads évidemment, boom!). C'est probablement ce qui est arrivé à Ross (pas assez de réputation pour répondre à la réponse, mais je sauvegarde les arguments thread-safe et non thread-safe)

1
Vasilis Nicolaou

D'accord, bien que je puisse extraire l'ancien code du contrôle de source qui a causé ces problèmes.

Je pense qu'il serait juste de dire que même lors de la synchronisation à la création, il existe des circonstances où un autre thread peut modifier les collections internes. Il vaut donc mieux être prudent. En regardant l'ancien code, oui, il utilisait en fait un convertisseur de messages. Mais uniquement lors de la synchronisation lors de la création.

restTemplate = new RestTemplate();

restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

Après cela, la seule interaction avec RestTemplate était avec ceci:

return restTemplate.postForObject(url, object, clazz);

C'est également la ligne qui lève finalement l'exception.

Il n'y a bien sûr aucune interaction avec le convertisseur de messages (nous n'avons aucune référence locale à lui).

En regardant la stacktrace et le code source de Spring, l'erreur s'est produite sur cette ligne:

for (HttpMessageConverter<?> converter : getMessageConverters()) {

Alors qu'est-ce que nous avons?

  1. Nous avons un accès simultané au message
  2. Si notre code ne l'a pas fait, alors quel code l'a fait? Je n'ai pas de réponse. Ma solution à l'époque était de créer un nouveau RestTemplate à chaque fois car les performances n'étaient pas un problème dans cette application.

Donc, en résumé, il y a des circonstances où les choses peuvent ne pas être thread-safe, certainement si vous jouez directement avec les convertisseurs de messages. Ce cas est cependant étrange, mais j'ai pensé qu'il serait utile de le publier.

0
Ross