====== TIBCO Enterprise Message Service 4.X ======
Messagingové služby (dále jen EMS) hrají důležitou roli v rozsáhlejších obchodních aplikacích, protože umožňují
* oddělit komponenty systému od sebe;
* komunikaci mezi komponentami, i když není spojení perzistetní;
* posílání asynchronních zpráv.
Mezi hlavní výhody EMS patří
* jednoté médium pro posílání zpráv;
* nezávislost na čase (zpráva bude klientovi doručena jakmile se připojí);
* zajištění doručení zpráv (na rozdíl například od tradičního posílání v TIBCO Rendezvouz);
* směrování, konverze z jednotlivých formátů;
* jednoduchost, škálovatelnost, nezávislost na platofrmě;
* možnost nastavování QoS.
Technologie TIBCO EMS 4.X je založena na technologii JMS 1.1 a tento produkt firmy TIBCO doplňuje celou rodinu messagingových systémů, jako je například TIBCO Rendezvouz, TIBCO SmartSockets nebo TIBCO Ajax Messaging Service. Kromě toho firma TIBCO protokol JMS rozšiřuje, nicméně kompatibilita s jinými JMS systémy je zajištěna.
TIBCO EMS 4.1 je založena na technologii JMS 1.1, která je popsána v samostatném článku [[Java Message Service 1.1]]. Server od TIBCA je napsán z výkonnostních důvodů v jazyce C. Pro nastavování je k dispozici několik configuračních souborů, command-line nástroj, logy a statistiky. Server můžete spravovat také nástrojem TIBCO Hawk.
===== Konfigurace =====
Probíhá pomocí sady conf souborů a konfiguračním nástrojem. S ním můžete dělat veškeré úkony (některé dokonce lze provádět jen tímto programem a ne editací souborů), ukládání do souborů je zajištěno automaticky (nebo při volání příkazu //commit//). Ukázka:
# tibemsadmin
TIBCO Enterprise Message Service Administration Tool.
Copyright 2003-2007 by TIBCO Software Inc.
All rights reserved.
Version 4.4.1 V2 2/21/2007
Type 'help' for commands help, 'exit' to exit:
> connect
Login name (admin):
Password:
Connected to: tcp://localhost:7222
tcp://localhost:7222> create topic chat trace=body,expiration=5
Topic 'chat' has been created
tcp://localhost:7222> create user test password=blahblah
tcp://localhost:7222> create group testing
tcp://localhost:7222> grant topic chat testing subscribe,publish,durable,purge
tcp://localhost:7222> q
Co se týká bezpečnosti, tam se nastavuje u front práva //receive, send, browse// a u topiců pak //subscribe, publish, durable, use_durable//. Jména jednotlivých permissions jsou vševysvětlující, za zmínku stojí snad jen durable (právo vytvářet durable příjemce) a use_durable (právo pouze se připojovat k již existujícím durable topicům). Tyto informace jsou uloženy v souboru ''acl.conf''.
Je možné použít zástupné znaky, v tomto případě se práva **dědí**, tj. podcíle mají stejná práva, která lze sice rozšiřovat, ale už **nelze omezit** již existující právo. Odebírá se příkazem //revoke//, ale pro tento příkaz platí dříve řečené -- nelze odebírat zděděná práva. Stejně tak nelze odebírat práva, která jsou definovány ve skupině (lze odebrat pouze celou skupinu). Veškeré operace prováděné administrativní konzolí jsou dynamické -- není třeba restartovat server (tedy kromě některých výjimek -- ty ale nejsou u ACL).
Práva se kontrolují **při každé akci** (nejen u přihlášení), jediným rozdílem je topic se zástupnými znaky. Tam se subscriber může přihlásit kdykoli, aniž by měl na to práva. Vlastní kontrola probíhá až při rozesílání zpráv, protože jinak by nebylo možné nastavovat práva již existujícím dlouhodobě přihlášeným.
===== Cíle =====
Cíle v EMS mohou být:
* statické -- vytvořené předem;
* dočasné -- temporary (obvykle pro request-response messaging);
* dynamické -- pokud klient chce vytvořit cíl, který neexistuje, server jej alokuje dynamicky až do ukončení sezení.
Na cílech administrátor nastavuje různé voby, příklad:
* společné volby -- failsafe, secure, maxbytes;
* volby pro frontu -- exclusive, prefetch, import;
* volby pro topic -- import, export.
Cílem (destination) v JMS rozumíme **topic** nebo **queue**. U TIBCO EMS rozdělujeme cíle na **statické** a **dynamické**. Dynamické cíle lze vytvořit pomocí administrační konzole nebo v klientské aplikaci pomocí //Session.createTopic(name)// a tyto cíle zaniknou v okamžiku uzavření všech spojení respektive vypnutí serveru, pokud je vytvořen přes konzoli. Naopak statické cíle jsou zapsány v konfiguračních souborech (ty se vytvářejí ručně nebo za pomoci konzole) a jsou dostupné, na rozdíl od dynamických, přes JNDI. Dynamické cíle mají v TIBCO EMS Adminovi (tibemsadmin) na začátku jména hvězdičku a všechny vlastnosti dědí od svého předka.
Existují ještě dočasné (temporary) cíle, ty se používají pro odpovědi při komunikaci request/reply nebo u přemostění. Při přemostění (bridging) se možné nakonfigurovat cíl tak, aby byly zprávy přeposílány na jiné cíle stejného či jiného typu. Je možné přemostit i na více cílů.
Cíle v TIBCO EMS maají mnoho vlastností, které se nastavují v adminovi nebo v konfiguračních souborech. Srhnuji je v přehledné tabulce:
|**vlastnost**|**topic**|**queue**|**popis**|
|failsafe|ano|ano|Při nastavení //normal// jsou zprávy zapisovány na disk asynchronně, což je rychlejší. Pokud je nemožné, aby při výpadku došlo ke ztrátám, pak je zde //failsafe// -- operace zápisu neskončí, dokud nejsou data na disku.|
|secure|ano|ano|Při nastavení server ověřuje uživatele, v opačném případě je možno připojit se anonymně. Tato volba //nemá nic společného// s SSL! Aby vešla proměnná v platnost, musí být také na serveru nastavena hodnota //authorization//.|
|maxmsgs|ne|ano|Maximum zpráv, které mohou být v cíli uloženy.|
|maxbytes|ano|ano|Maximální prostor, které mohou všechny zprávy v jednom cíli na serveru zabrat.|
|overflowPolicy|ne|ano|Pokud je nastavena na //discardOld//, pak jsou staré zprávy při překročění některého z limitů umazávány. Pokud není nastaveno, server vrací chybu a zprávu už nepřijme.|
|global|ano|ano|Zprávy přicházející na tento cíl jsou automaticky routovány na servery, které se podílejí na routování.|
|sender_name|ano|ano|Vkládá do z práv jméno odesílatele do vlastnosti JMS_TIBCO_SENDER, ale pouze v případě, že producent nenastaví vlastnost JMS_TIBCO_DISABLE_SENDER. Při anonymní komunikaci je vloženo jméno uživatele, který vytvořil cíl.|
|sender_name_enforced|ano|ano|Při nastavení je nařízeno odesílateli jméno neskrývat.|
|flowControl|ano|ano|Specifikuje pool pro nezpracované zprávy, které vznikají, když konzumenti nestíhají zpracovávat. Například flowControl=32MB.|
|trace|ano|ano|Aktivuje sledování (tracing), buď se volba jen uvede nebo nastaví na hodnotu //body// -- v tomto případě se sledují i těla zpráv.|
|import|ano|ano|Povoluje přijímat zprávy z cizích prostředí TIBCO Rendezvous a SmartSockets.|
|export|ano|ne|Povoluje odesílat do cizích prostředí.|
|maxRedelivery|ne|ano|Počet pokusů při neúspěšném doručení zprávy z fronty. Zprávy mají nastavenou vlastnost JMSRedelivered na true a počet je uložen ve JMSXDeliveryCount. Pokud se nepodaří zprávu doručit, je zahozena, nebo uložena ve frontě nedoručených zpráv, pakliže byla nastavena vlastnost zprávy JMS_TIBCO_PRESERVE_UNDELIVERED.|
|exclusive|ne|ano|V tomto režimu server odesílá jen prvnímu odběrateli fronty. Pokud ale odeslání selže, zpráva je poslána dalšímu v pořadí. V non-exclusive režimu server rozesílá pomocí round-robin -- postupně všem připraveným příjemcům|
|prefetch|ne|ano|Povoluje konzumentovi předčítat zprávy do fáze //fetch// než jsou zpracovány klientským kódem ve fázi //accept//. Možné hodnoty jsou //none// (vypnuto), 0 (zděděno) nebo 1 a více (maximum zpráv ve fázi fetch).|
|expiration|ano|ano|Přepisuje hodnotu nastavenou ve zprávě. Určuje životnost zprávy (např. 10min), zadání 0 znamená nesmrtelnost.|
Při přihlášení na topic (pouze a jen přihlašení na topic) je možné specifikovat **zástupné znaky** (wildcards), například foo.* nebo foo.bar.>. Pokud budou takové topicy existovat, budou příjemci chodit zprávy ze všech vyhovujících topiců. Zástupný znak ''*'' funguje jen na jednu úroveň (foo.bar.foo nevyhovuje foo.*), znak '>' i na podúrovně. Na fronty není možné použít zástupné znaky, pouze je lze aplikovat v konfiguračních souborech aby byla konfigurace stručnější.
Vlastnosti a přístupová práva cílů se **dědí**, ale není možné již nastavenou hodnotu z předka přenastavit. Co se jednou nastavít je pevně dáno a //nelze to změnit// (výjimka je pouze o vlastnosti maxbytes).
===== Bridging =====
Na cílech můžeme //v rámci jednoho EMS serveru// vytvářet **přemostění**. Každý cíl lze přemostit (zkopírovat) na jeden nebo více cílů stejného i různého typu -- konzumenti ze zdrojového cíle nepoznají, že byla zpráva přemostěna. Přemostění není tranzitivní a specifikuje se v souboru ''bridges.conf''. Zdrojový cíl může obsahovat zástupné znaky. Na přemostění lze klást podmínky pomocí MessageSelector syntaxe (podobná SQL92).
Na následující ukázce vytvoříme jednoduché přemostění -- cokoliv bude posláno do fronty //uue.orders// s JMS hlavičkou //detail_log// nastavenou na příslušnou hodnotu, tak bude přemostěno do fronty //uue.loggers.orders//, kde budou zprávy čekat na vyřízení (například nějakým loggerem):
[topic:uue.orders]
queue=uue.loggers.orders selector="detail_log in ('true', 'yes')"
U přemostění není třeba nastavovat práva -- stačí, když producent má právo poslat zprávu do zdrojového i konečného cíle. Přemostění není třeba nijak nastavovat co se týká bezpečnosti.
Pomocí **řízení běhu** (flow control) lze omezovat velikost poolu nezpracovaných zpráv. Při přemostěných spojeních to lze aplikovat jak na zdrojovém tak i cílovém serveru, ale u front to není relevantní. V případě že dojde k překročení na cílovém serveru, zprávy nejsou přeposílány až do té doby, než še situace zlepší. V případě překročení na zdrojovém serveru jsou další zprávy ihned odmítány.
===== Routing =====
TIBCO EMS nabízí možnost routingu, tedy **směrování zpráv**, a to v následujících scénářích:
* **topic** zprávy mohou být směrovány jeden i více //hopů//;
* **queue** zprávy mobou být směrovány jeden hop z/do domovské queue.
Pod pojmem //hop// rozumím transfer zprávy ze serveru na server. Například pokud budu mít směrování mezi třemi servery ''A-B-C'', tak v cestě od serveru A k serveru C mám dva hopy (//A-B// a //B-C//).
Routing je vždy **obousměrný**, to znamená, že routované cíle se navzájem "synchronizují". Pokud nastavíme routování topicu T1 mezi servery A a B, klienti mohou publikovat na kterémkoli z nich, zpráva se zpropaguje na ten druhý. Routing lze ale vytvořit pouze na cíli, který má nastaven **global** -- viz výše.
Routování není možné nakonfigurovat tak, že by byl jeden server dostupný z druhého //více než jednou cestou//. Kdybych se měl vyjádřit pojmem z teorie grafů, tak graf navzájem propojených serverů musí být //acyklický//. TIBCO EMS při startu kontroluje tento fakt a nespustí se, pokud existuje v grafu kružnice.
Pro lepší návrh routingu jsou v produktu TIBCO EMS dostupné tzv. zóny -- zóna je množina směrovacích pravidel. Každé směrovací pravidlo tedy patří do nějaké zóny a lze vytvéřet několik typů zón:
* mhop -- multi-hop -- v této zóně zprávy cestují, dokud existují pravidla;
* 1hop -- one-hop -- v této zóně cestují zprávy jen jeden hop.
Zóny se ovšem týkají **pouze topiců**, u front je to jedno, protože jak bylo uvedeno, fronta může mít jen //jeden hop//. Díky zónám (konkrétně 1hop) lze tedy vytvořit komplexní routování (které může obsahovat i kružnice). Zóny se mohou i překrývat -- pokaždé, když je zpráva předávána na hranici zóny je čítač hopů vynulován. Je nutné mít excelentní představivost, či spíše tužku a papír. Zóny jsou zkrátka kolečka, do kterých zakroužkujeme jednotlivé uzly grafu.
===== Failover =====
TIBCO EMS podporuje plný failover na dvou serverech. Záložní server číhá v závětří a kontroluje (heartbeat failure nebo connection failure), zda je primární server on-line. Jakmile se stane havárie a spojení se přeruší //všichni klienti jsou automaticky přesměrováni na záložní server//, ten se postará také o doručení všech PERSISTENT nedoručených zpráv. Primární server se po naběhnutí stává záložním serverem, takže servery si //vymění role//. Konzumenti topiců (i durable) //nepoznají rozdíl//, konzumenti front však mohou zaznamenat problém.
Může totiž nastat situace, kdy volání //acknowledge()// metody vyhodí výjimku //IllegalStateException//. V tomto případě by aplikace měla volat //Session.recover()//, což způsobí restart konzumace. Aplikace musí být přípravena na to, že z fronty může přijít zpráva s nastaveným flagem //JMSRedelivered// na //true//.
Podobně u transakcí může nastat výjimka při volání //commit()//. Vyvolá se //TransactionRolledBackException// a aplikace musí zprávy poslat znovu.
Failover funguje pouze a jen tehdy, když mají oba servery **sdílená data**. To znamená sdílejí informace o spojeních na primární server, metadata ohledně dodávky zpráv a databázi zpráv. Toto se zajišťuje pomocí přístupu na stejný souborový systém, ten však musí splňovat některé požadavky (write order, synchronous write persistence, distributed file locking a unique write ownership). Proto se pro tyto účely používají dual-channel RAID, NAS nebo SAN úložná řešení (clusterová). ((Poznámka: Toto řešení failoveru s centrálním úložným prostorem má i několik nevýhod. Jako dvě hlavní se mi jeví cena řešení (je nutno obstarat kvalitní storage rešení -- použití NFSv3 nemusí být spolehlivé a dostatečně výkonné) a také fakt, že veškerá data jsou jen na jednom místě. Při výpadku centrálního úložného systému je konec. Jiné JMS systémy tohle řeší dvoufázovými transakcemi, kde data jsou zapsána na dvou místech současně.))
Servery //mohou sdílet// také některou konfiguraci (typicky všechny soubory kromě tibemsd.conf), ale v tomto případě je důležité povolit zápis do konfigurace pouze primárnímu serveru (například právy na filesystému), aby si servery nechtěně nepřepisovali jednu a tatáž konfiguraci. Při aktivaci nového primárního serveru server //automaticky// konfiguraci znovu načte.((Je důležité mít nastaveny správně timeouty na klientech, aby přečkali čas výpadku. Na to se nesmí zapomínat, v implicitní konfiguraci je to v pořádku.))
Servery **musejí mít stejné jméno** (volba ''server'' v konfiguraci) a také musejí mít nastavenu hodnotu ''ft_active'' na "ten druhý" server (ostrý na záložní, záložní na ostrý). Záložní systém ve stand-by režimu nepřijímá spojení (kromě administrativních). Klienti musejí být nakonfigurováni tak, aby se připojovali na jeden ze serverů (musejí mít předem seznam všech serverů). Příklad:
ConnectionFactory cf = new TibjmsTopicConnectionFactory(
"tcp://server1:7222,tcp://server2:7222");
V ideálním případě při použití JNDI stačí nakonfigurovat factory:
create factory MyTopicFactory topic url=tcp://one:7222,tcp://two:7222
===== Podpora SSL =====
TIBCO EMS umí SSL -- komunikace s klienty (pozor -- jedině z .NETu nelze komunikovat po SSL) i mezi servery (failover, routing), konfigurace je jednoduchá. Úplně nějjednodušší je nastavit certifikát serveru, soukromý klíč (pokud není uvnitř p12 souboru) a heslo k certifikátu (pokud je zašifrovaný):
listen = ssl://7243
ssl_server_identity = certs\server.cert.pem
ssl_server_key = certs\server.key.pem
ssl_password = password
Uvedený příklad načetl ukázkový certifikát, který se dodává v instalaci. Nyní je komunikace šifrovaná a klient si může také ověřit identitu serveru, pokud bude mít k dispozici ověřený kořenový certifikát včetně celého chainu. Na klientovi můžeme použít SSL přímo (a nastavit potřebné parametry pomocí továrny), nebo využít JNDI -- v tomto případě se musí vytvořit pro SSL spojení speciální JNDI ConnectionFactory. Ukázka přímého spojení pomocí SSL:
import com.tibco.tibjms.TibjmsConnectionFactory;
import javax.jms.*;
public class DirectConnection {
public static void main(String args[]) throws JMSException {
// vytvoř Factory přímo
TibjmsConnectionFactory cf =
new TibjmsConnectionFactory("ssl://localhost:7243");
// nebudeme kontrolovat hostname
cf.setSSLEnableVerifyHostName(false);
// načteme kořenový certifikát
cf.setSSLTrustedCertificate("c:\\TIBCO\\ems\\bin\\certs\\server_root.cert.pem");
// vytvoříme konexi s uživatelským jménem a heslem (na serveru je zapnutá autentifikace)
Connection conn = cf.createConnection("test", "test");
// další postup je již stejný jako při použití JNDI
Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topicChat = sess.createTopic("chat");
MessageProducer producer = sess.createProducer(topicChat);
MessageConsumer consumer = sess.createConsumer(topicChat);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
try {
System.out.println(((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
conn.start();
TextMessage msg = sess.createTextMessage();
msg.setText("JMS zpráva, bla bla");
producer.send(msg);
// počkáme sekundu, než zpráva dorazí
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
conn.close();
}
}
Možnosti v konfiguraci SSL jsou široké, TIBCO umí autentifikovat jak server, tak klienta. Celé je to postaveno nad knihovnou OpenSSL. Další informace jsou v dokumentaci.
===== Napojení na TIBCO Rendezvous =====
EMS je schopen importovat i exportovat zprávy s TIBCO Rendezvous (dále jen TRV) přes **topic** a také importovat přes **queue** (export přes queue není možný). Nejdříve je třeba nadefinovat transport v souboru ''transports.conf'':
# ukazka transportu TRV
[RV01]
# typ (tibrv nebo tibrvcm - certifikovane RV)
type = tibrv
# RV klienti nemohou nastavovat JMSDeliveryMode, proto se to musi nastavit zde
topic_import_dm = TIBEMS_RELIABLE
queue_import_dm = TIBEMS_PERSISTENT # treti moznosti je TIBEMS_NON_PERSISTENT
# export hlavicek a vlastnosti je defaultne zapnuty
export_headers = true
export_properties = true
# nastaveni spojeni (protokol:host:port)
service = 7780
network = lan0
daemon = tcp:host5:7885
# ukazka certifikovaneho RV transportu
[RVCM01]
type = tibrvcm
# odkaz na jiz definovany transport
rv_tport = RV01
# correspondent name
cm_name = RVCMTrans1
# databazovy soubor
ledger_file = ledgerFile.store
# nejprve uklada do souboru a pak posila
sync_ledger = true
# prijimat zpravy i kdyz byl odpojen
request_old = true
default_ttl = 600
A poté jej použít v topic/queue (zde pomocí admin nástroje):
addproc topic mytopic import="RV1_TRANS"
Výměna zpráv s TRV musí být **globálně zapnuta** pomocí volby //tibrv_transports// v ''tibemsd.conf''. Import zpráv do topicu probíhá pouze když jsou nějací subscribeři. Podobně se dá nakonfigurovat import pro queue (ale už ne export) -- tam probíhá import ihned, i když neexistují žádní odběratelé.
Při **importu** z TRV je nastaven JMSDestionation na RV subject a JMSReplyTo na RV reply subject. Při exportu transport seskupí všechny JMS (kromě dvou zmíněných) a vloží je do submessage //JMSHeaders//, pokud není nastaveno ''export_headers'' na //false//. Podobně je to s //JMSProperties//.
Při importu se nastavují vlastnosti JMS_TIMBCO_IMPORTED na true a JMS_TIBCO_MSG_EXT na true v případě, že zpráva //může// obsahovat podzprávy nebo typ pole (array field). V případě importu certifikované zprávy jsou pak ještě nastaveny JMS_TIBCO_CM_PUBLISHER (correspondent name) a JMS_TIBCO_CM_SEQUENCE (indikuje číslo zprávy).
V těle zprávy se mapují zprávy typu BytesMessage, ObjectMessage, StreamMessage, TextMessage na stejné typy v RV. Typ MapMessage se mapuje do nejvyšší úrovně zprávy RV.
Pomocí knihoven ''tibrvjms.jar'' a ''tibrvjweb.jar'' je možné, aby RV klienti komunikovali přímo s EMS serverem bez použití ''rva''.
===== Napojení na TIBCO SmartSockets =====
Možnost importu a exportu přes **topic**, pouze importu do **queue**. To je stejné jako u RV. Konfigurace je též stejná, ale globální proměnná se jmenuje ''tibss_transports'':
[SS01]
type = tibss
# jedno nebo více spojení na SS servery (protokol:hostname:port)
server_names = tcp:rtHost2A:5555, ssl:rtHost2B:5571
username = emsServer6
password = heslo
# jmeno projektu (default je rtworks)
project = sales_order_entry
# load balancing režim
lb_mode = round_robin
override_lb_mode = enable
# další volby (popsáno v dokumentaci)
delivery_mode = gmd_some
===== Odkazy =====
* http://www.tibco.com/software/messaging/enterprise_messaging_service -- produktové stránky firmy TIBCO
* http://www.kosek.cz/lenka/articles/messaging.html -- úvod do messagingu
* http://www.tibco.com/services/educational/certification/TB0-104.jsp -- certifikat firmy TIBCO