Le chiamate in proxy non funzionano come previsto

5

Ho modificato un'applicazione per avere una divisione client / server più pulita per consentire la suddivisione del carico e la condivisione delle risorse, ecc. Tutto è scritto su un'interfaccia, quindi è stato semplice aggiungere un livello remoto all'interfaccia usando un proxy. Tutto ha funzionato bene. La fase successiva consisteva nell'aggiungere un livello di caching all'interfaccia e ancora una volta questo ha funzionato bene e la velocità è stata migliorata, ma non tanto quanto mi sarei aspettato. All'ispezione è diventato molto chiaro cosa stava succedendo.

Sono certo che questo comportamento è stato visto molte volte in passato e probabilmente c'è un modello di progettazione per risolvere il problema ma sfugge a me e non sono nemmeno sicuro di come descriverlo.

È più facile spiegarlo con un esempio. Immaginiamo che l'interfaccia sia

interface IMyCode {
    List<IThing> getLots( List<String> );
    IThing getOne( String id );
}

Il metodo getLots () chiama getOne () e riempie l'elenco prima di tornare.

L'interfaccia è implementata sul client che è inoltrata a un client remoto che chiama quindi il server remoto che a sua volta chiama l'implementazione sul server. Al livello client e server esiste anche una cache.

Quindi abbiamo: -

Client interface
      |
 Client cache
      |
 Remote client
      |
 Remote server
      |
  Server cache
      |
Server interface

Se chiamiamo getOne ("A") sull'interfaccia client, la chiamata viene passata alla cache del client che presenta dei guasti. Questo quindi chiama il client remoto che passa la chiamata al server remoto. Questo quindi chiama la cache del server che fa anche il guasto e quindi la chiamata viene passata all'interfaccia del server che in effetti riceve l'IThing. A sua volta la cache del server viene riempita e infine anche la cache del client.

Se getOne ("A") viene nuovamente chiamato nell'interfaccia client, la cache del client ha i dati e viene restituito immediatamente. Se un secondo client chiamato getOne ("B") riempirebbe la cache del server con "B" così come la propria cache client. Quindi, quando il primo client chiama getOne ("B"), la cache del client fa un errore ma la cache del server ha i dati.

Questo è tutto come ci si aspetterebbe e funziona bene.

Ora consente di chiamare get get (["C", "D"]). Funziona come ci si aspetterebbe chiamando getOne () due volte, ma qui c'è una sottigliezza. La chiamata a getLots () non può fare direttamente uso della cache. Pertanto la sequenza consiste nel chiamare l'interfaccia client che a sua volta chiama il client remoto, quindi il server remoto e infine l'interfaccia del server. Questo quindi chiama getOne () per riempire l'elenco prima di tornare.

Il problema è che le chiamate getOne () sono soddisfatte sul server quando idealmente dovrebbero essere soddisfatte sul client. Se si immagina che il collegamento client / server sia molto lento, allora diventa chiaro il motivo per cui la chiamata del client è più efficiente della chiamata del server una volta che la cache del client ha i dati.

Questo esempio è ideato per illustrare il punto. Il problema più generale è che non è possibile continuare ad aggiungere layer proxy a un'interfaccia e aspettarsi che funzioni come si potrebbe immaginare. Non appena la chiamata passa "attraverso" il proxy, tutte le chiamate successive sono sul lato proxy invece che sul lato "self".

Non ho imparato o non ho imparato qualcosa correttamente?

Tutto questo è implementato in Java e non ho usato EJB.

Sembra che l'esempio possa essere fonte di confusione. Il problema non ha nulla a che fare con l'efficienza della cache. Ha più a che fare con un'illusione creata dall'uso di proxy o tecniche AOP in generale.

Quando si ha un oggetto la cui classe implementa un'interfaccia, si presume che una chiamata su quell'oggetto possa effettuare ulteriori chiamate sullo stesso oggetto. Ad esempio,

public String getInternalString() {
    return InetAddress.getLocalHost().toString();
}

public String getString() {
    return getInternalString();
}

Se ottieni un oggetto e chiama getString () il risultato dipende da dove il codice è in esecuzione. Se si aggiunge un proxy remoto alla classe, il risultato potrebbe essere diverso per le chiamate a getString () e getInternalString () sullo stesso oggetto. Questo perché la chiamata iniziale viene "deproxied" prima che venga chiamato il metodo effettivo.

Trovo questo non solo confuso, ma mi chiedo come posso controllare questo comportamento, specialmente perché l'uso del proxy può essere di terze parti.

Il concetto va bene, ma la pratica non è certamente quella che mi aspettavo.

Ho perso il punto da qualche parte?

    
posta AndyH 01.11.2011 - 11:55
fonte

2 risposte

1

La relazione tra due metodi è sconosciuta a una libreria di proxy.

Quindi il metodo

public String getString() {
  return getInternalString();
}

una volta proxy e implementato sul lato server è equivalente a qualsiasi metodo

public String getString() {
  //some executable statements along with a return.
}

Ma se vuoi mantenere la relazione tra i metodi, allora potresti voler non eseguire il proxy dei metodi più alti

public String getString() {

o

List<IThing> getLots( List<String> );

ogni client della libreria proxy codificherà per questo separatamente, cioè non fa più parte dell'interfaccia IMyCode che dovrebbe essere supportata dalla libreria proxy.

    
risposta data 04.12.2011 - 08:21
fonte
0

Sto sbagliando tutto questo in fretta prima di parlare, quindi scusami se sono un po 'poco chiaro

Penso che il problema principale sia che la tua chiamata getLots() va sostanzialmente in un ciclo composto da più% chiamate digetOne(). Senza vedere il tuo codice e la tua infrastruttura in dettaglio, sarei tentato di fare in modo che la chiamata di getLots() faccia esattamente questo, ottenere molti oggetti in una singola chiamata, caricando le tue due cache lungo la strada.

Sì, c'è un costo iniziale unico per questa chiamata, ma in seguito le cache sono più complete, rendendo le chiamate getOne() successive molto più probabili a essere più veloci e anche le chiamate getLots() verosimilmente più veloci (a seconda della chiave di ricerca gamma). Ovviamente devi pensare a come assicurarti che getLots() chiami in modo efficiente per cercare e restituire dalla cache e scendere i livelli come richiesto.

Sembra che tu misuri le prestazioni prima e dopo aver apportato le modifiche, il che è positivo. Puoi utilizzarlo per misurare la dimensione più efficiente delle chiamate getLots() che fai al server (quando non sono presenti cache o cache parziale o cache completa).

    
risposta data 01.11.2011 - 13:21
fonte

Leggi altre domande sui tag