Newsgroup: it.lavoro.informatica
Titolo: [JEE] chiamare servizio esterno

Scusate, ma in Java EE se dovete chiamare un servizio esterno quale sarebbe la best practice?
 
Ad esempio, il browser chiama l'application server, l'appliaction server chiama  il servizio esterno e ottiene la risposta, l'application server passa la risposta al browser.
 
In questa catena di eventi, se il servizio di terze parti comincia a fare le bizze mi tiene il thread principale bloccato. Se arrivano diverse chiamate il thread pool si esaurisce.
 
Come ci si ripara da un problema del genere? 


Architettura asincrona, sempre che il servizio di terze parti abbia la 
possibilità di notificarti in automatico oppure on-demand (vedi ad 
esempio le notifiche IPN + WebHooks di Paypal oppure il gestore delle 
transazioni di Steam / Apple).
 
M.
 

 In questa catena di eventi, se il servizio di terze parti comincia a fare le bizze mi tiene il thread principale bloccato.
 > Se arrivano diverse chiamate il thread pool si esaurisce.
 
La comunicazione con il sistema di terze parti dovrebbe essere asincrona 
e basata su callback, e comunque dovresti andare in time-out su 
connessioni sincrone.
 
Il tuo server, poi, potrebbe implementare una back pressure sul modello 
del circuit breaker:
 
https://martinfowler.com/bliki/CircuitBreaker.html
 
 
-- 
4ndre4

 sarebbe la best practice?
 
una best practice sempre valida è il non-blocking (asynchronous) i/o. per 
dirla in breve la chiamata al ws la fai in modo asincrono.
intanto in questo modo il ws che va in cianfanelle non mette in crisi il 
sistema, che non mi pare poco. 

 possibilità di notificarti in automatico oppure on-demand
 
...e sarebbe molto strano se non lo facesse. Io direi che quando si 
progetta un servizio asincrono, o lo si rende idempotente, o bisogna 
implementare entrambi i comportamenti che citi (notifica + on-demand).
 
Se A invia la richiesta a B, quest'ultimo, oltre a notificare A, deve 
anche consentire ad A di recuperare lo stato della richiesta.
 
L'alternativa e` che il comportamento di B sia idempotente.
 
A, se non ha mai ricevuto una risposta da B, puo` o richiedere lo stato 
della richiesta e riprovare (se B non e` idempotente), o inviare la 
stessa richiesta (se B e` idempotente), e io direi che il secondo caso 
dovrebbe essere la regola.
 
-- 
4ndre4


Il servizio non e' asincrono, e non ci sono possibilita' di ricevere notifiche. D'altrocanto non si tratta di un operazione che normalmente impiega molto tempo  quindi giustamente il provider non ha pensato di renderlo asincrono (ritorna una lista di pagamenti).


Pero' questo significherebbe rendere l'intera richiesta asincrona, altrimenti c'e' sempre un thread che rimane bloccato. Nel mio caso la view e' gestita dal framework JSF, che (afaik) non supportra meccanismi di push.
 
Correggimi se sbaglio, ma si tratterebbe di mettere in piedi una serie di step quali: aprire una long running http connection usando una servlet asincrona, far partire la chiamata REST (asincrona), salvare in memoria la risposta, spedire al browser una sorta di "hey! la risposta e' pronta", far partire una request/response dal browser per renderizzare la pagina con JSF.
Un gran casino. By the way, esiste qualche library per evitarmi di fare tutto a mano?


Grazie, mi sa che vado per il CircuiBreaker.
 
Dopotutto non ho crucci per quanto riguarda il throughput, quindi posso anche fare a meno di usare meccanismi non blocking. Se succedono casini sul servizio con questo pattern mi salvo il sedere :)
 
Visto che ci sono ne approfitto: siccome trovo piuttosto interessante l'argomento, questa soluzione l'hai letta sul blog del Fowler o su qualche libro?

 Il servizio non e' asincrono, e non ci sono possibilita' di ricevere notifiche.
 > D'altrocanto non si tratta di un operazione che normalmente impiega 
molto tempo
 > quindi giustamente il provider non ha pensato di renderlo asincrono 
(ritorna una lista di pagamenti).
 
Pero`, come vedi, la natura sincrona di quel servizio puo` causare 
problemi. Il punto non e` se impieghi poco/tanto tempo per rispondere. 
Il punto e` che non deve tener bloccato il client.
 
-- 
4ndre4

 Visto che ci sono ne approfitto: siccome trovo piuttosto interessante l'argomento, questa soluzione l'hai letta sul blog del Fowler o su qualche libro?
 
E` uno stability pattern piuttosto comune oggigiorno. Lo trovi 
praticamente ovunque. Nota che se usi Spring, hai Spring Retry che 
implementa un meccanismo di back pressure molto potente, configurabile 
via annotations.
 
http://www.javaworld.com/article/2824163/application-performance/stability-patterns-applied-in-a-restful-architecture.html
 
 
-- 
4ndre4

On 09/04/2017 11:05, 
laghi...@gmail.com
 wrote:
 
 >[...]Nel mio caso la view e' gestita dal framework JSF, che (afaik) 
non supportra meccanismi di push.
 
Ma nessuno ti vieta di usare JavaScript lato client, in maniera 
asincrona, o no?
 
-- 
4ndre4

 di push.
 
si ovvio l'intera richiesta diventa asincrona, il thread del pool esce 
subito tornando disponibile per accettare nuove richieste mentre la 
richiesta corrente viene passata ad una future. in questo modo le 
richieste lente restano (ovviamente) lente, ma non fanno natta.
 
che questo possa essere un problema con le jsf sinceramente non ricordo, 
sarà un decennio che non le uso anche se le rimembranze sono che avevano 
un ciclo di vita privo di ogni versatitlità. quindi non escludo una 
reingegnerizzazione.
 


No, nessuno lo vieta. Ma appunto verrebbe utilizzato solo per ricevere l'evento "risposta pronta" e fare un'altra chiamata AJAX via JSF. Il tutto per mostrare una risposta in condizioni normali ci impiegherebbe meno di un secondo (parlo del servizio terze parti). Che poi c'e' anche il problema di capire dove mettere la risposta asincrona da recuperare nella chiamata AJAX successiva.
 
Per questo motivo mi sto orientando verso sincrono + meccanismi di back pressure come mi hai mostrato.


Ho visto che RichFaces ha un componente <a4j:push>, ma mi sembra un gimmick piu' che altro. E' vero, riceve la risposta json tramite push, ma poi fa un'ulteriore chiamata per renderizzarla dentro il normale ciclo JSF. Inoltre richiede JMS. 
 
Preferisco farmelo a mano a questo punto.

 Che poi c'e' anche il problema di capire dove mettere la risposta asincrona da recuperare nella chiamata AJAX successiva.
 
Il tuo sistema dovrebbe essere stateless, mentre tu stai cercando di 
implementare qualcosa di stateful.
 
C e` il tuo client, S il tuo server ed X il sistema esterno.
 
Al momento di mandare la richiesta, C mostra, mettiamo, uno spinner, 
incapsula la richiesta in un costrutto promise, e continua a fare quel 
che deve fare. S riceve la richiesta in una servlet (non deve essere 
asincrona se usi una promise lato client), chiama il servizio esterno X 
passando una callback, e continua a fare quel che deve fare. Ora, gli 
scenari sono due:
 
1) X non invoca mai la call back => la richiesta di S va in timeout => 
il branch di errore si sblocca in C e viene mostrato un errore;
 
2) X ritorna con i dati => la risposta viene mandata a C => il branch di 
successo si sblocca in C e i dati vengono elaborati;
 
 > Il tutto per mostrare una risposta in condizioni normali ci 
impiegherebbe meno di un secondo (parlo del servizio terze parti).
 
Capisco che non vuoi sparare alle mosche con un cannone, ma le 
architetture, in informatica, si progettano proprio per mettersi al 
riparo da quelle situazioni anormali che possono causare problemi, come 
nel tuo caso. Se si assumesse che tutto andra` bene nella maggior parte 
dei casi e che non convenga spendere energie per implementare qualcosa 
che possa resistere alle eccezioni, gran parte dei pattern 
architetturali che abbiamo puo` andarsi a far benedire.
 
-- 
4ndre4

 Ho visto che RichFaces ha un componente <a4j:push>, ma mi sembra un gimmick piu' che altro.
 
Dipende che intendi per gimmick. Lato client non esiste modo di ricevere notifiche push vere (a4j:push le emula con delle poll periodiche) se non usando WebSockets. Non mi e` ancora chiaro perche` tu non possa usare JavaScript e cosa c'entrino le JSF con la logica lato client.
 

 architetturali che abbiamo puo` andarsi a far benedire.
 
In effetti la soluzione sopra ottimizza thread e risorse, ma dovrei stravolgere troppo. Cioe' in realta' non posso fare uso di js tanto facilmente.


Per il push puoi anche tenere la connessione http aperta e spedire i dati un po' alla volta. Per il mio caso va bene anche il long polling comunque.
 
http://www.javaworld.com/article/2077995/java-concurrency/java-concurrency-asynchronous-processing-support-in-servlet-3-0.html
 
 
Per quanto riguarda JSF, ho provato a spiegarlo nell'altro post.
 
 
 

 In effetti la soluzione sopra ottimizza thread e risorse, ma dovrei stravolgere troppo.
 
In questo caso...
Pero`, in generale, ti sconsiglio di tenere aperte connessioni troppo a 
lungo e gestire il tutto manualmente. Secondo me sei arrivato al punto 
da dover rivedere l'architettura della tua applicazione.
 
-- 
4ndre4

 
http://www.javaworld.com/article/2077995/java-concurrency/java-concurrency-asynchronous-processing-support-in-servlet-3-0.html
 
 
Si`, il long polling e` una tecnica utilizzabile. L'event system di un 
mio precedente progetto era implementato a quel modo. Non mi fa 
impazzire, pero`. Preferisco STOMP su WebSocket:
 
http://jmesnil.net/stomp-websocket/doc/
 
 
-- 
4ndre4

 Il servizio non e' asincrono, e non ci sono possibilita' di ricevere notifiche. D'altrocanto non si tratta di un operazione che normalmente impiega molto tempo  quindi giustamente il provider non ha pensato di renderlo asincrono (ritorna una lista di pagamenti).
 
Mah... appunto perchè impiega molto tempo a maggior ragione 
l'architettura asincrona ci sta a fagiolo. Ah, mi sa che c'era del 
sarcasmo "il provider non ha pensato di renderlo asincrono" ... :-)
 
Il client fa la richiesta, il provider risponde 202 o simili "OK, ho 
preso in incarico la tua richiesta e ti farò sapere..." e poi entrano in 
gioco callback & C.
Steam ad esempio non ti notifica una cippa, devi andar tu a vedere se ha 
finito di processare la richiesta...

 ...e sarebbe molto strano se non lo facesse.
 
L'OP o ha abusato di sarcasmo oppure il provider veramente non c'ha 
pensato...

 gioco callback & C.
 
il problema delle callback è quando fra client e provider c'è di mezzo un 
firewall, per esempio se il provider è su internet e il client no.
 

>il problema delle callback è quando fra client e provider c'è di mezzo un 
firewall, per esempio se il provider è su internet e il client no. 
 
Dipende da come la callback è implementata. Se per callback si intende un URL che passi al servizio di terze parti esterno, hai ragione. Se si intende la callback usata, per esempio, da una servlet asincrona, non hai bisogno di aprire nulla.

Giustamente una ceppa :)
Se va giù la comunicazione tra il consumer e il provider, che si fa?
Hai modo di recuperare lo stato di una transazione?


Si e' un servizio di lettura quindi idempotente. 
 
Ci sono anche servizi di scrittura (charging di una carta). Anche in quel caso puoi ottenere lo stato del charging successivamente (infatti posso attaccare metadati all'operazione e interrogarli successivamente). 
Se la comunicazione si interrompe, il client solleva un'eccezione che posso utilizzare per programmare un nuovo charge (non prima di aver controllato se l'ultimo e' andato bene o male).
Questa parte la faccio "offline", in un thread schedulato.

 Questa parte la faccio "offline", in un thread schedulato.
 
Che vuol dire "la faccio offline"? Se il tuo sistema A chiama B e B 
cade, come fa A a sapere se B ha elaborato la richiesta o meno?
 
-- 
4ndre4

 firewall, per esempio se il provider è su internet e il client no.
 
Regola nel firewall, autenticazione e signature dei dati...
 

 cade, come fa A a sapere se B ha elaborato la richiesta o meno?
 
 
Se non ho capito male (di nuovo) l'OP lavora in ambito pagamenti. Una 
delle best practice se una richiesta va gambe all'aria e non sai se è 
stata elaborata, nel dubbio per non farla duplicata si manda una 
richiesta di cancellazione della richiesta originaria: anche se la 
richiesta è stata autorizzata (ma tu non potevi saperlo perché magari 
c'è stato un time out) fai la richiesta di cancellazione e il client 
dovrà ripetere la richiesta. Un po' come succede al supermercato quando 
passano un item due volte per sbaglio...
 
Oppure come ha detto l'OP fai il polling delle transazioni e ti regoli 
di conseguenza.
 

 richiesta di cancellazione della richiesta originaria
 
Ma una richiesta di cancellazione puo` andare bene solo le la richiesta e` ancora pending, i.e. non e` stata ancora processata dal sistema ricevente, altrimenti diventa difficile (se non impossibile) fare un rollback di tutti i processi a cascata. In realta`, la best practice sarebbe quella di rendere il sistema idempotente.

 Se non ho capito male (di nuovo) l'OP lavora in ambito pagamenti.
 
si ma ha anche specificato che quel servizio è di sola lettura. non fa 
trasazioni, e ci mancherebbe, se il sistema di pagamento consentisse di 
pagare in questo modo cioè invocando semplicemente un servizio avrebbe 
ben altri problemi che non il semplice esaurimento del thread-pool...


Con "Offline" ho inteso fuori dal ciclo di request/response iniziato dal browser. Nel mio caso c'e' uno @Scheduled task ripetuto ogni x minuti che va a pigliarsi uno per uno i pagamenti da fare (questi sono salvati su db) e fa una chiamata sincrona per ciascuno. Se va a buon fine marco il record con "Done", altrimenti "Error".
 
Per gli errori non so ancora cosa fare. Prima di riprovare, potrei interrogare il servizio per vedere se effettivamente ha fatto qualcosa o meno.

 ben altri problemi che non il semplice esaurimento del thread-pool...
 
In che senso? Dipende sempre da qual e` il sistema di pagamento di cui 
si sta parlando. Con sistemi come Skrill/Moneybookers, per esempio, ci 
si invoca il loro servizio.
 
-- 
4ndre4


Beh la mia domanda originale era su un sevizio di sola lettura. Ma si possono fare anche addebitamenti da carte sempre usando una chiamata sincrona.
Che problema ci sarebbe? 
 
Comunque trattasi di Stripe.

On 10/04/2017 21:28, 
laghi...@gmail.com
 wrote:
 
 > potrei interrogare il servizio per vedere se effettivamente ha fatto 
qualcosa o meno.
 
Appunto, e` quel che ti chiedevo: il servizio ti consente di ottenere 
quell'informazione?
 
-- 
4ndre4


Si. Allora dovrebbe tornare un id. Se non torna niente perche' la comunicazione va giu' potrei usare un metadato che gli ho passato (un guid ad esempio) per interrogare lo stato dell'addebito. Se non ha salvato manco quello significa che non ha fatto nulla. Altrimenti sono coglioni loro :D


è chiaro, ma per esempio un'apertura del firewall (anche punto punto) non 
in tutte le strutture la puoi proporre. almeno non senza rogne.
un nostro cliente perché un applicativo riceveva una invocazione da un 
provider esterno (con apertura puntuale) prima il consulente della 
sicurezza pretendeva di metterlo in dmz, desistendo poi per una questione 
di buonsenso,  poi ha preteso di sottoporlo ai test periodici di security 
assessment, in particolare verificando la resilienza ad un attacco ddos 
che puntualmente non supera finendo al tappeto (non per colpa sua, 
porello, è deployato sulla loro infrastruttura ed è quella che non 
regge...).


beh sicurezza ovviamente. i sistemi di pagamento mica si fanno dare gli 
estremi della carta dal merchant: il merchant inizia la transazione, poi 
manda l'utente a pagare sul sistema di pagamento e infine quest'ultimo 
richiama il merchant al redirect-url registrato per confermare.
 

Non ci crederai, ma Skrill (all'epoca chiamato Moneybookers) mom registrava la transazione su db prima di rispondere, per cui se chiedevi lo stato di una transazione e non la trovavano poteva significare che o non era stata ancora registrata o la richiesta non era mai avvenuta, cosa che generava grattacapi niente male.

 In realta`, la best practice sarebbe quella di rendere il sistema idempotente.
 
Vero, bisogna vedere se il servizio di terze parti lo preveda. Paypal ad 
esempio usa il "nonce": un token che ti dà Paypal usa-e-getta che metti 
nella richiesta. Se lo usi due volte e la prima richiesta è stata 
elaborata (quale sia il risultato non ha importanza) Paypal ti dice 
ciccia...
Altri provider hanno sistemi diversi, ad esempio un header HTTP custom.
 
Da parte del client è buona norma usare un codice di "reference" diverso 
per ogni richiesta, ma il pattern consueto è quello che poi i provider 
se ne fottono se lo usi n-volte per la stessa richiesta... non fa fede 
per l'idempotenza, sta a te non fare casini.
 
M.

 richiama il merchant al redirect-url registrato per confermare.
 
Nì, dipende se vuoi essere dentro il PCI scope (ci sono diversi livelli) 
oppure no. Quello di cui parli è un tipo di pagamento chiamato "hosted", 
mentre l'altro richiede che gli estremi di pagamento in qualche modo li 
ottieni e gestisci tu, al provider interessa sapere solo a chi 
addebitare la spesa.
 
Ma credo che nella richiesta dell'OP quale sia l'architettura, "hosted" 
o diretta, faccia poca differenza. I timeout li becchi sia nelle 
transazioni hosted che dirette: alla fine ti ritrovi nel tuo DB con una 
"reference" che non sai che fine ha fatto (success o failure).
 
M.