Je tente d'utiliser la méthode de synchronisation dans le contrôleur de printemps. Parce que notre passerelle de paiement utilise la méthode [@RequestMapping (valeur = "/ pay", méthode = RequestMethod.POST)] différentes transactions [txn id: txn01 & txn02] à la fois. Mais ces 2 transactions différentes traitent une par une que parallèle en raison de l’utilisation du bloc de synchronisation.
Problème -> La raison pour laquelle j’utilise le bloc synchronize dans l’automate est que Transaction [txn01] frappe [@RequestMapping (value = "/ pay", méthode = RequestMethod.POST)] deux fois comme un appel en double depuis la passerelle de paiement. avant de terminer le premier appel [traitement du backend], je reçois un deuxième appel de la passerelle de paiement pour le même identifiant de tran.
Existe-t-il un moyen de traiter deux transactions différentes en parallèle avec l'utilisation de l'identifiant de transaction dans un bloc de synchronisation autre qu'un appel en double? S'il vous plait conseillez-moi.
S'il vous plaît laissez-moi savoir si ma question n'est pas claire.
@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
synchronized (this) {
return this.processPayAck(httpRequest, httpResponse, session);
}
}
public synchronized String processPayAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
// Payment Acknowledgment process here
if (sametranIDNotExists) {
// first call here
callWS(); - processing business logic.
return someURL;
} else {
// Gets second call here before first call completed
return someURL;
}
}
Code modifié:
Est-il correct d'utiliser intern dans le bloc synchronize?.
@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
String tranID = httpRequest.getParameter("tranID");
synchronized (String.valueOf(tranID).intern()) {
return processPayAck(httpRequest, httpResponse, session);
}
}
Je ne sais pas si vous travaillez dans un environnement distribué.
S'il n'y a qu'un seul ordinateur, vous pouvez supprimer le mot clé syncronized et créer à la place basé sur le nom avec l'identifiant de la transaction.
Si ce programme fonctionne dans un cluster et qu'il y a plusieurs machines, ce qui signifie que la demande peut être affectée à une machine différente, je pense que vous devez utiliser aquaire distribution-lock avec Redis ou d'autres frameworks.
Le bloc synchronisé est utilisé pour assurer la sécurité des threads. De même, lorsque plusieurs threads tentent d'accéder au même objet, seuls les threads verrouillés au niveau de l'objet peuvent accéder au bloc synchronized(this)
. Tandis qu'un membre d'un groupe de threads est verrouillé au niveau de l'objet, le reste des threads attend (les threads accèdent au bloc synchronisé un par un mais pas en parallèle).
Utilisation appropriée: Utilisez un bloc synchronisé lorsque les threads tentent de modifier la même ressource (pour éviter l'incohérence des données). Dans ce cas, les threads tentent de modifier la même ressource de base de données. Mais comme mentionné, les modifications sont effectuées sur 2 transactions différentes (lignes).
Si modifier une ligne ne nuit pas à l’autre, il n’est pas nécessaire d’utiliser la ligne.
return this.processPayAck(httpRequest, httpResponse, session);
dans le bloc synchronisé. Au lieu de cela, il pourrait être écrit comme:
@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
return this.processPayAck(httpRequest, httpResponse, session);
}
Suggestion: Utilisez CopyOnWriteArrayList
(en tant que variable d'instance non une variable locale) pour stocker l'ID de transaction à la fin de la méthode payAck
et utilisez la méthode contains("textId")
pour vérifier si l'ID de transaction donné utilise à nouveau la méthode payAck
.