Tagged: Java

Implementace distribuovaného zámku využívající Redis databáze


Redis databáze není jen tak ledasjaká obyčejná věc, dá se využít k implementaci distribuovaného zámku.

O jednu takovou jsem se pokusil v knihovně https://github.com/abelaska/jedis-lock.

Základem je využití vlastnosti funkce SETNX a tou je SET IF NOT EXISTS, tzn. že uloží hodnotu do požadovaného klíče pouze za podmínky, že klíč v daný moment v databázi neexistuje. Využívá se faktu, že Redis funkce jsou atomické.

Postup pro získání zámku je následující:

  1. SETNX lock.name <aktuální Unix timestamp + lock timeout + 1>, pokud je návratová hodnota 1 byl zámek úspěšně získán, v opačném případě se pokračuje dalším krokem.
  2. GET lock.name, provede se kontrola expirace zámku porovnáním hodnoty klíče zámku s aktuálním časem, tzn. že pokud je hodnota klíče zámku menší než aktuální čas je zámek expirovaný a pokračuje se dalším krokem. Pokud zámek expirovaný není pokračuje se krokem 4.
  3. GETSET lock.name <aktuální Unix timestamp + lock timeout + 1>, pokud funkce navrátí stejnou hodnotu jaká byla získaná v předchozím kroku, byl zámek úspěšně získán a zároveň nastaven nový korektní čas expirace. Pokud zámek nebyl získán pokračuje se následujícím krokem.
  4. Funkce počká 100 milisekund a poté pokračuje 1. krokem. Kroky se opakují dokud neuplyne timeout pro získání zámku (defaultní hodnota je 10 sekund).

Nevýhodné je tohle řešení ve chvíli kdy proces alokuje zámek a potom je ukončen aniž zámek uvolní. V tomto případě ostatní procesy zbytečně dlouho čekají než zámek expiruje. Jeden se s tím musí naučit žít.

Pokud používáte Apache Maven postačí do projektu doplnit novou závislost.

<dependency>
    <groupId>com.github.jedis-lock</groupId>
    <artifactId>jedis-lock</artifactId>
    <version>1.0.0</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

V příkladu je získán a následně uvolněn zámek s názvem lock.name. Timeout pro získání zámku nastaven na 10 sekund a expirace zámku na 30 sekund.

Jedis jedis = new Jedis("localhost");
JedisLock lock = new JedisLock(jedis, "lock.name", 10000, 30000);
lock.acquire();
try {
  // do some stuff
}
finally {
  lock.release();
}

A nakonec jsem využil https://oss.sonatype.org Maven repositář pro deploy knihovny do Maven Central repositáře. A to přesně podle postupu uvedeném na stránkách Sonatype OSS Maven Repository Usage Guide.

No a to je asi tak všechno. Enjoy 😉

Reklamy

Java způsob získání statistiky aplikace v App Engine


Jak jsem psal v předchozím příspěvku, pracoval jsem na knihovně, která stáhne z Google App Engine detaily kvót a limitů pro sledování aplikace.

Dnes jsem tuto knihovnu pojmenovanou jako gae-app-manager dokončil a udělal release ve verzi 0.0.1.

Můžete ji najít na http://sourceforge.net/projects/gae-app-manager/

Vydal jsem ji i jako utilitu pro použití v konzole (http://sourceforge.net/projects/gae-app-manager/files/0.0.1/net.sf.gae-app-manager-0.0.1-jar-with-dependencies.jar/download).

Návod pro kompilaci utility http://gae-app-manager.sourceforge.net/.

Enjoy.

$ java -jar net.sf.gae-app-manager-0.0.1-jar-with-dependencies.jar
Usage: java -jar gae-app-manager.jar [full gmail address] [account password] [appspot application name]

Ex.:
java -jar gae-app-manager.jar alois.belaska@gmail.com heslo eshopsengine

$ java -jar target/net.sf.gae-app-manager-0.0.1-SNAPSHOT-jar-with-dependencies.jar alois.belaska@gmail.com ****** eshopsengine
Quotas are reset every 24 hours. Next reset: 19 hours
REQUESTS_CPU_TIME[0.00/6.50 (0%) CPU hours,OKAY]
REQUESTS_COUNT[2/1333328 (0%),OKAY]
REQUESTS_OUTGOING_BANDWIDTH[0.00/1.00 (0%) GBytes,OKAY]
REQUESTS_INCOMING_BANDWIDTH[0.00/1.00 (0%) GBytes,OKAY]
REQUESTS_SECURE_COUNT[0/1333328 (0%),OKAY]
REQUESTS_SECURE_OUTGOING_BANDWIDTH[0.00/1.00 (0%) GBytes,OKAY]
REQUESTS_SECURE_INCOMING_BANDWIDTH[0.00/1.00 (0%) GBytes,OKAY]
STORAGE_DATASTORE_API_CALLS[0/10368000 (0%),OKAY]
STORAGE_BLOBSTORE_API_CALLS[0/10368000 (0%),OKAY]
STORAGE_TOTAL_STORED_DATA[0.00/1.00 (0%) GBytes,OKAY]
STORAGE_BLOBSTORE_STORED_DATA[0.00/1.00 (0%) GBytes,OKAY]
STORAGE_DATA_SENT_TO_DATASTORE_API[0.00/12.00 (0%) GBytes,OKAY]
STORAGE_DATA_RECEIVED_FROM_DATASTORE_API[0.00/116.00 (0%) GBytes,OKAY]
STORAGE_DATASTORE_CPU_TIME[0.00/62.11 (0%) CPU hours,OKAY]
MAIL_MAIL_API_CALLS[0/7000 (0%),OKAY]
MAIL_RECIPIENTS_EMAILED[0/2000 (0%),OKAY]
MAIL_ADMINS_EMAILED[0/5000 (0%),OKAY]
MAIL_MESSAGE_BODY_DATA_SENT[0.00/0.06 (0%) GBytes,OKAY]
MAIL_ATTACHMENTS_SENT[0/2000 (0%),OKAY]
MAIL_ATTACHMENT_DATA_SENT[0.00/0.10 (0%) GBytes,OKAY]
URLFETCH_API_CALLS[0/657084 (0%),OKAY]
URLFETCH_DATA_SENT[0.00/4.00 (0%) GBytes,OKAY]
URLFETCH_DATA_RECEIVED[0.00/4.00 (0%) GBytes,OKAY]
IMAGEMANIPULATION_API_CALLS[0/864000 (0%),OKAY]
IMAGEMANIPULATION_DATA_SENT_TO_API[0.00/1.00 (0%) GBytes,OKAY]
IMAGEMANIPULATION_DATA_RECEIVED_FROM_API[0.00/5.00 (0%) GBytes,OKAY]
IMAGEMANIPULATION_TRANSFORMATIONS_EXECUTED[0/2592000 (0%),OKAY]
MEMCACHE_API_CALLS[0/8640000 (0%),OKAY]
MEMCACHE_DATA_SENT_TO_API[0.00/1.00 (0%) GBytes,OKAY]
MEMCACHE_DATA_RECEIVED_FROM_API[0.00/5.00 (0%) GBytes,OKAY]
XMPP_API_CALLS[0/657084 (0%),OKAY]
XMPP_DATA_SENT[0.00/4.00 (0%) GBytes,OKAY]
XMPP_RECIPIENTS_MESSAGED[0/657084 (0%),OKAY]
XMPP_INVITATIONS_SENT[0/1000 (0%),OKAY]
TASKQUEUE_API_CALLS[0/100000 (0%),OKAY]
DEPLOYMENTS_COUNT[0/1000 (0%),OKAY]