スプリングコントローラーで同期メソッドを使用しようとしています。ペイメントゲートウェイは、メソッド[@RequestMapping(value = "/ pay"、method = RequestMethod.POST)]に異なるトランザクション[txn id:txn01&txn02]を一度にヒットするためです。ただし、同期ブロックを使用しているため、これら2つの異なるトランザクション処理は、並列処理よりも1つずつ処理されます。
問題->コントローラーで同期ブロックを使用しているのは、トランザクション[txn01]が[@RequestMapping(value = "/ pay"、method = RequestMethod.POST)]を2回呼び出すと、ペイメントゲートウェイからの重複呼び出しのようになるためです。最初の呼び出しを終了する前に[バックエンド処理]同じトランザクションIDの支払いゲートウェイから2番目の呼び出しを取得します。
重複した呼び出し以外の同期ブロックでトランザクションIDを使用して2つの異なるトランザクションを並列処理する方法はありますか?同じトランザクションIDを意味します。私にアドバイスしてください。
不明な点がありましたらお知らせください。
@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;
}
}
変更されたコード:
同期ブロック内でインターンを使用する正しい方法ですか?.
@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);
}
}
分散環境で作業しているかどうかはわかりません。
マシンが1つしかない場合は、syncronizedキーワードを削除して、代わりにトランザクションIDで name-based ロックを作成できます。
このプログラムがクラスターで動作しており、複数のマシンがある場合、つまりリクエストが別のマシンに割り当てられる可能性がある場合、Redisまたは他のフレームワークで distribution-lock を取得する必要があると思います。
同期ブロックは、スレッドセーフを提供するために使用されます。また、複数のスレッドが同じオブジェクトにアクセスしようとしている場合、オブジェクトレベルのロックのみのスレッドがsynchronized(this)
ブロックにアクセスできます。スレッドのグループの1つがオブジェクトレベルのロックを取得している間、残りのスレッドは待機します(スレッドは同期ブロックに1つずつアクセスしますが、並列にはアクセスしません)。
適切な使用:スレッドが同じリソースを変更しようとしているときに同期ブロックを使用します(データの不整合を回避するため)。この場合、スレッドは同じデータベースリソースを変更しようとしています。ただし、前述のとおり、2つの異なるトランザクション(行)で変更が行われます。
1つの行を変更しても他の行に悪影響を与えない場合は、行を使用する必要はありません。
return this.processPayAck(httpRequest, httpResponse, session);
同期ブロック内。代わりに、次のように書くことができます。
_@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
return this.processPayAck(httpRequest, httpResponse, session);
}
_
Suggestion:CopyOnWriteArrayList
を使用して(ローカル変数ではなくインスタンス変数として)、payAck
メソッドの最後にトランザクションIDを格納し、contains("textId")
メソッドを使用します指定されたトランザクションIDがpayAck
メソッドを再度使用しているかどうかを確認します。