2010-04-20 2 views
10

Ho questa situazione .... Comunicazione SOAP 1.1 avviata dal client tra un server e diciamo, decine di migliaia di client. I client sono esterni, entrano attraverso il nostro firewall, autenticati dal certificato, https, ecc. Possono essere ovunque, e di solito hanno i loro firewall, router NAT, ecc ... Sono veramente esterni, non solo uffici aziendali remoti. Potrebbero essere in una rete aziendale/campus, DSL/via cavo, anche in dialup.Quale protocollo di rete utilizzare per la notifica leggera di app remote?

Client utilizza Delphi (2005 + SOAP correzioni rispetto al 2007), e il server è C#, ma da un/design punto di vista dell'architettura, che dovrebbe non importa.

Attualmente, i clienti spingere i nuovi dati al server e tirare i nuovi dati dal server in 15 minuti di ciclo di controllo. Il server attualmente non spinge i dati - il client colpisce il metodo "messagecount", per vedere se ci sono nuovi dati da estrarre. Se 0, dorme per altri 15 minuti e controlla di nuovo.

Stiamo cercando di ottenere che fino a 7 secondi.

Se questo fosse un app interno, con una o solo poche decine di clienti, ci piacerebbe scrivere un cilent servizio di sapone "ascoltatore", e spingerebbe i dati ad esso. Ma dal momento che sono esterni, siedono dietro i loro firewall e, a volte, reti private dietro router NAT, questo non è pratico.

Quindi siamo rimasti con il polling su un ciclo molto più veloce. I client 10K, ognuno dei quali controlla il proprio messagecount ogni 10 secondi, saranno messaggi di 1000/sec che per lo più sprecheranno solo risorse di larghezza di banda, server, firewall e autenticatore.

Così sto cercando di progettare qualcosa di meglio di quello che sarebbe pari a un attacco DoS auto-inflitta.

non credo che sia pratico di avere i messaggi SOAP server di inviare al client (push) in quanto ciò richiederebbe troppo configurazione sul lato client. Ma penso che ci siano alternative che non conosco. Ad esempio:

1) Esiste un modo per il client di effettuare una richiesta per GetMessageCount() tramite Soap 1.1, e ottenere la risposta, e quindi forse "rimanere in linea" per forse 5-10 minuti a ottenere risposte aggiuntive nel caso in cui arrivino nuovi dati? cioè il server dice "0", quindi un minuto dopo in risposta a qualche trigger SQL (il server è C# su Sql Server, btw), sa che questo client è ancora "sulla linea" e invia il conteggio messaggi aggiornato di "5 "?

2) C'è qualche altro protocollo che potremmo usare per "ping" il cliente, utilizzando le informazioni raccolte dalla loro ultima richiesta GetMessageCount()?

3) Non lo so nemmeno. Immagino che sto cercando un protocollo magico in cui il client può inviare una richiesta GetMessageCount(), che includerebbe informazioni per "oh a proposito, nel caso in cui la risposta cambi nell'ora successiva, ping a questo indirizzo ... ".

Inoltre, suppongo che uno di questi schemi "mantieni la linea aperta" avrebbe un serio impatto sul dimensionamento del server, in quanto sarebbe necessario mantenere aperte molte migliaia di connessioni contemporaneamente. Ciò potrebbe avere un impatto anche sui firewall, credo.

C'è qualcosa là fuori in questo modo? O sono abbastanza bloccato con il polling?

TIA,
Chris

UPDATE 4/30/2010:
hanno dimostrato che avere 7-seconda notifica non è né facile né economico, soprattutto senza andare al di fuori dello standard aziendale di HTTPS/SOAP/Firewall , probabilmente stiamo per lanciare una soluzione a due fasi. Phase1 invierà che i client eseguano il polling "su richiesta" con il GetMessageCount eseguito tramite SOAP, niente di eccezionale qui. Ci sarà un pulsante "Aggiorna" per estrarre nuovi dati (che è ragionevole qui, dato che l'utente di solito ha motivo di sospettare che i nuovi dati siano pronti, cioè hanno appena cambiato il colore del tessuto nel sistema online, in modo che sappiano fare clic AGGIORNA prima di visualizzare il manifest di spedizione sul desktop, e ora vedono il colore nella descrizione.) (Questa NON è davvero un'app per abbigliamento/moda, ma hai un'idea). La nozione di avere i due aps sempre sincronizzati, con aggiornamenti in tempo reale inviati dall'host, è ancora sul tavolo, utilizzando le tecnologie discusse qui. Ma mi aspetto che verrà spinto per un'altra versione, in quanto possiamo fornire l'85% delle funzionalità senza doverlo fare. Tuttavia, spero che potremo fare una dimostrazione del concetto e possiamo dimostrare che funzionerà. Tornerò e pubblicherò aggiornamenti futuri. Grazie per l'aiuto di tutti su questo.

+0

Ciao Ragazzi, ho aggiunto una taglia, ma questo non significa che non mi piacciono le risposte finora! Volevo essere sicuro che questo fosse guardato il più possibile, e inoltre, ho sempre desiderato fare un premio ... Ci sono un sacco di ottime risposte qui, e lo apprezzo davvero. –

+0

Vorrei poter dividere il premio ... ci sono molte grandi risposte qui, e forse il più utile a questo punto del progetto è il consenso travolgente che concorda con la mia posizione secondo cui non possiamo gestire connessioni 10K facendo polling veloce, né possiamo trasmettere loro dati come ascoltatori SOAP regolari. –

risposta

3

I due grandi partiti sullo sviluppo multi-tier a Delfi sono components4developers (con il loro kbmMW prodotto descritto nella risposta da Mark Robinson) e RemObjects con il loro prodotto RemObjects SDK (hanno un bell'esempio che potrebbe essere simile a quello che vuoi: Push notifications for iPhone).

Nel tuo ambiente complesso, l'UDP multi-cast potrebbe non tagliarlo, ma da una prospettiva generale è imbattibile.

Se una connessione è aperta, può essere utilizzata in un modo bidirezionale (utilizzato anche da .NET remoting e WCF), ma ha un overhead aggiuntivo.

È necessario trovare un equilibrio tra il mantenimento delle connessioni in tempo reale (blocco delle risorse) e la creazione di nuove connessioni (tempo di costo e latenza).

--jeroen

+0

Grazie - in realtà abbiamo una licenza per RemObjects qui, è stata acquistata per un progetto che non è stato decollato. Lo scaverò e guarderò il loro esempio. –

+0

@Chris: Se risolvi il problema, facci sapere come lo hai risolto! –

+0

+ Accettato in quanto questa sarà probabilmente la nostra soluzione. Vorrei poter dividere la taglia su questo e molti altri raccomandando RemObjects e kbmMW. Ringrazia tutti! –

4

avrei uno sguardo al kbmMW

avrei eventualmente utilizzare un metodo simile a MS Exchange - il collegamento e l'autenticazione tramite TCP/IP, quindi la notifica di aggiornamento (s) dal server al client tramite UDP, quindi il client riceve la richiesta udp e scarica l'aggiornamento tramite tcp/ip.

(Almeno è così che ho capito funziona MS Exchange)

+0

Bello - ci proverò. Mi piace la parte relativa a: "utilizzando la messaggistica basata su pubblicazione/sottoscrizione tramite più tipi di code di messaggi completamente configurabili e accessibili agli sviluppatori." –

+0

La tua notifica udp suona proprio come quello che sto cercando. Sai se sarà in grado di tornare attraverso la stessa porta del firewall? Sto sperando in una configurazione zero qui (zero in cima a quello di cui hanno già bisogno per SOAP su https). –

+0

Ho un grande amore per kbmMW - è un po 'una curva di apprendimento, ma in un certo senso ha senso - versione base gratuita! –

1

Le notifiche push per iPhone funziona solo se i dispositivi remoti sono iPhone. Le uniche altre opzioni sono mantenere una connessione aperta (sebbene in gran parte inattiva) o il polling dal client.

Potrebbe essere possibile ridurre il sovraccarico del polling semplificando la chiamata. Utilizzare una semplice azione Web per restituire il numero massimo di messaggi al client e fare in modo che il client esegua un semplice HTTP GET per ricevere questo numero. Ciò riduce la quantità di larghezza di banda e la mantiene semplice. Se poi il cliente ha bisogno di ottenere dati aggiornati, è possibile effettuare una chiamata di sapone completa.

1

Ogni volta che si dispone di un server e di oltre 10.000 clienti e si ha bisogno di aggiornarsi ogni pochi secondi si verificheranno problemi. Otterrei qualche altro server e manterrò i client connessi su un thread in background nel client che inizialmente si connette e aspetta che le notifiche arrivino dal server con un meccanismo keep-alive incorporato.

Se si sta tentando di passare dal server a un client non attualmente connesso, quindi buona fortuna se non si ha alcun controllo sugli ambienti dei client. Mi sembra che tu sia forzato nelle connessioni avviate dal client.

3

Si potrebbe provare a effettuare una chiamata al server e attendere sul server per un po 'di tempo (1 minuto?) Fino ad avere alcuni aggiornamenti. In questo modo non hai bisogno di una connessione da server a client e ottieni risultati quasi istantanei sul client (se hai aggiornamenti entro 1 minuto, termini la chiamata di attesa). È un parente facile e ampiamente utilizzato (?) Dalle app web (come Gmail: ha una connessione in background come questa: se arriva una nuova email la vedi immediatamente nella tua casella di posta!). Io uso qualcosa di simile (RemObjects):

function TLoggingViewService.GetMessageOrWait: TLogMessageArray; 
begin 
    if (Session.Outputbuffer.Count > 0) or 
    //or wait till new message (max 3s) 
    Session.Outputbuffer.WaitForNewObject(3 * 1000) 
    then 
    //get all messages from list (without wait) 
    Result := PeekMessage; 
end; 

punto negativo è: si mantiene la connessione aperta per un lungo tempo relativo (? Che cosa succede se la connessione persa a causa di wifi, ecc) e "carico" di server alto (ogni connessione ha una discussione, è mantenuta aperta: se hai molti clienti puoi uscire dalle risorse).

Qui usiamo RemObjects e usiamo TCP + Binmessage che ha un overhead MUCH MUCH inferiore a SOAP + HTTP ed è molto veloce! Quindi, se puoi usarlo, posso davvero raccomandarlo! (nel tuo caso hai bisogno di Remobjects per Delphi e RemObjects per .Net). Utilizzare SOAP solo se è necessario connettersi a terze parti e utilizzare HTTP solo se necessario a causa di Internet/firewall. SOAP è bello ma ha problemi di prestazioni e sovraccarico.

È inoltre possibile utilizzare una combinazione di questi: una connessione TCP (RemObjects) semplice (con sovraccarico basso) in un thread in background, il polling di ogni 10 secondi e attendere 5 secondi per i nuovi dati.

7

Considerare "giocare" il protocollo HTTP un po 'per ottenere ciò che si vuole pur essendo in grado di andare oltre tutti i proxy e NAT e firewall di uno potrebbe avere sul lato client.

Fare in modo che ogni singolo client esegua una semplice richiesta HTTP per il conteggio dei messaggi in modo da inibire qualsiasi tipo di memorizzazione nella cache (esempio: GET http://yourserver.org/getcount/nodeid/timeofday/sequence). Nell'implementazione lato server del server HTTP, il ritardo fornisce la risposta se il "conteggio" è lo stesso di un tempo (cioè: nessun nuovo messaggio).

Ho fatto questo per un'applicazione Ajax in esecuzione in un browser e si comportava un po 'come un'applicazione di chat, ma la tua soluzione può essere ancora più veloce. Ho implementato il lato server usando il server TIdHttp e questo mi ha permesso di ritardare effettivamente la risposta alle cose del cliente semplicemente dormendo() nella sua thread. Dal lato del client sembrava un server che a volte è veramente lento a dare una risposta.

Pseudocodice per la roba sul lato server:

function ClientHasMessages(ClientID:Integer; MaxWait:TDateTime):Boolean; 
var MaxTime:TDateTime; 
begin 
    if ClientActuallyHasMessage(ClientID) then Result := True 
    else 
    begin 
     MaxTime := Now + MaxWait; 
     while Now < MaxTime do 
     begin 
     if ClientActuallyHasMessage(ClientID) then 
      begin 
      Result := True; 
      Exit; 
      end 
     else 
      Sleep(1000); 
     end; 
     Result := False; // TimeOut 
    end; 
end; 

L'idea alla base di questo codice: Viene eseguito in un thread sul proprio server, dove si può testare il numero di messaggi, presumibilmente, per molto poco costo:

  • Non provoca traffico di rete durante l'attesa.
  • Non utilizza CPU durante la sospensione.
  • Permetterà all'utente di conoscere il suo messaggio molto rapidamente.
  • Permette il controllo client quanto tempo l'attesa potrebbe essere (il cliente dovrà aumentare la quantità di tempo che il server può ritardare la risposta fino a quando non riceve più la risposta, e poi fare un passo indietro un po '- in questo modo il protocollo si adatta alla qualunque sia il buggy router NAT utilizzato dal client).
  • È possibile scappare con lunghi periodi di assenza di comunicazioni TCP/IP ed essere ancora in grado di fornire la risposta all'istante. 30 secondi sono fatti facilmente e per i clienti con router NAT buoni può essere molto più lungo.

Le dimensioni verso il basso di questo sarebbe i requisiti sul server, ma sono tentato di dire che sono fattibile:

  • implementazione TCP/IP del server ha bisogno di tenere traccia di un certo numero di connessioni simultanee (ogni client avrà una richiesta HTTP attiva in ogni momento). La mia macchina NAT Linux sta monitorando le connessioni 15K in questo momento ed è praticamente inattiva, quindi potrebbe funzionare.
  • Il server avrebbe una discussione aperta per ogni singola richiesta HTTP client, in ogni momento: Ancora una volta, il Server 2008 "Workstation" che sto usando per scrivere questo (grazie MSDN per avermi permesso di fare cose così oltraggiose) ha circa 1500 thread attivi ed è anche fondamentalmente inattivo ...
  • A seconda della tecnologia utilizzata per il codice lato server, MEMORY potrebbe essere il fattore limitante.
3

ho fatto test di prestazioni su sistemi ancora più grandi che i vostri clienti 10K, e quando si raggiunge gli importi di cui di richieste/sec te l'aspetti viso molto probabilmente con connessioni/sec, Concurrent connessioni aperte, i firewall diventando lento ecc. (Molto gli stessi problemi che un Torrent Tracker potrebbe affrontare).

Se i client devono solo "chiedere se c'è qualcosa di nuovo", il protocollo più leggero che sia facile da implementare è UDP, il prossimo più leggero sarebbe il TCP puro, entrambi che usano i client Indy.

Il protocollo stesso potrebbe effettivamente essere semplice come l'invio di "Niente di nuovo dal momento che [hh aaaa-mm-gg: mm: ss]" per il server, e rispondendo con un numero 1 byte (256 risposte possibili).

Con TCP si avrebbe il vantaggio di mantenere aperta la "pipe" per alcuni minuti, e si può inviare il messaggio "nulla di nuovo" ogni x secondi. Anche con TCP il server può "spingere" informazioni alla pipe (client) quando succede qualcosa, dato che il client sta controllando periodicamente i dati nella pipe.

+1

Dato che un pacchetto IP ha un po 'di overhead, non ha senso replicare con un singolo byte. Se si causa l'invio di un pacchetto, è sufficiente inviare di nuovo ciò che è necessario per trasferire le informazioni: 4 o 8 byte invece di 1, ad esempio non farà * quella * molta differenza ... – mghie

+0

Giusto, quindi si potrebbe effettivamente andare per il Cliente che si connette per poi ascoltare qualcosa per un po 'e disconnettersi se non c'è niente ... Il vero trucco è scegliere buoni valori per rimanere connessi, e quando riconnettersi di nuovo ... –

+0

Lungo queste linee, sto pensando che forse dovremmo installare un secondo server "messagecount", fuori dal firewall. Il server principale spingerebbe i conteggi dei messaggi ad esso quando cambiano. Quindi i client si collegano al server MessageCount utilizzando uno di questi altri metodi. Anche se facessimo ricorso alle chiamate http, non avrebbero infastidito i nostri firewall e autenticatori interni, poiché presumibilmente il messagecount potrebbe essere rappresentato in modo tale da non dover essere protetto. Interessante! –

2

Vorrei provare a distribuire il carico il più possibile tra diversi server. Per fare ciò, farei quanto segue:

  1. I client si registrano al tuo servizio per ricevere le notifiche. Ricevono un ID di sessione valido per un dato periodo di tempo (15 minuti).
  2. I server eseguiranno un controllo periodico su quale client registrato ha un messaggio in arrivo e genererà un elenco di tali client (tecnicamente, lo inserirò in un DB diverso nel DMZ).
  3. Si esegue un numero di server "push notification" che seguono un protocollo molto semplice: ottengono una query URL contenente l'ID sessione e risponde con una risposta HTTP breve: un 404 o un 200 con un (firmato) URL del server SOAP da indirizzare per afferrare i messaggi. Per prestazioni aggiuntive, è possibile utilizzare HTTP 1.1 e connessioni permanenti.
  4. Il client raggrupperà questi server di notifica push tutte le volte che lo desiderano. Dal momento che sono molto semplici e sono rigorosamente di sola lettura, possono rispondere alle domande molto velocemente e saranno facilmente scalabili.
  5. Se il client riceve una risposta 302, può connettersi al server SOAP corretto (è possibile utilizzarlo anche per la distribuzione del carico, se lo si desidera) ed estrarre i messaggi.

Dovrai fare attenzione alla sicurezza qui. Innanzitutto, ti suggerisco di NON utilizzare HTTPS per i server di notifica push. Invece, è possibile firmare il contenuto della risposta con una chiave di sessione scambiata quando il client ha richiesto le notifiche. Il cliente è quindi responsabile di convalidare la risposta. Non dimenticare che è necessario firmare non solo lo stato ma anche l'URL del servizio SOAP.

È un po 'complesso ma disaccoppiando lo stato e il traffico dei messaggi effettivi, è possibile ridimensionare la soluzione molto più facilmente. Inoltre, non sarà necessario passare attraverso la costosa negoziazione SSL fino a quando non si desidera effettivamente scambiare dati.

2

Usiamo "eventi" RemObjects SDK per questo, ma questo potrebbe non essere adatto a voi, perché

un: Funziona solo con RemObjects proprio protocollo binario, non SOAP (vale a dire i clienti devono incorporare il codice RO)

b: Fondamentalmente è un approccio "mantieni la linea aperta". quindi la scalabilità verso i client 10K è un potenziale problema.

Proverei alcuni test solo per vedere quale sovraccarico mantenendo i socket 10K aperti ha effettivamente. Se tutto ciò di cui hai bisogno è un paio di concerti di memoria extra del server, questa sarà una soluzione economica. E poiché il socket è aperto dal client, non dovrebbe causare problemi con il firewall. Il peggio che il firewall può fare è chiudere il socket, quindi il client dovrebbe riaprirlo quando ciò accade.

+0

Grazie, questo è l'approccio a cui mi sto proponendo, ma con una svolta: sto pensando di avere un server separato solo per le notifiche. Poiché le notifiche non contengono il payload effettivo dei dati (che rimarrebbe su SOAP, con https, autenticazione dei certificati attraverso il firewall, ecc.) E hanno solo un ID client e un conteggio dei messaggi, la sicurezza non è tanto preoccupazione. Quindi potremmo inserire questo nella DMZ o persino usare l'hosting esterno. cont ... –

+0

... Quindi ora diventa una questione di "quanti server sono necessari per gestire le pipe di notifica di 10K RemObjects", invece di "quanto costa $$$ sostenere i REQUISITI SOAP 10K, tramite https, con l'autenticazione CERT client, oltre il nostro ESB, fino al nostro database Sql * Server back-end? " –

+0

@Roddy, posso chiedere quanti clienti hai aperto contemporaneamente? E c'è un progetto di esempio che consiglieresti di guardare, usando RO SDK? Spero di avere un po 'di tempo per prototiparlo in poche settimane (sepolto con materiale di produzione in questo momento). –

0

Qualcosa un po 'fuori campo.

Perché l'autenticazione è richiesta solo per ottenere un flag che indica che gli aggiornamenti sono pronti? Perché non avere una macchina al di fuori del firewall di autenticazione ... o anche nel cloud ... che non fa altro che gestire le richieste 'è tutto disponibile'. Quindi, se qualcosa è disponibile, il cliente deve passare attraverso i cerchi per ottenere i dati reali. Questo server delle richieste potrebbe eseguire il conteggio dei 7 secondi dal server reale.

Ora stiamo parlando di pochissimi dati e molto poco tempo di configurazione per una semplice 'bandiera', nemmeno un conteggio.

Sono ancora migliaia per le richieste, ma migliaia di richieste con un sovraccarico minimo rispetto alla richiesta autenticata completa.

Problemi correlati