Hank Lin

A new blog

用terracotta達成tomcat Session Replication

| Comments

不設定session replication 的tomcat cluster

Tomcat是最常用的java servlet container了, 經過調教也有很多網站在使用. 如果要達成high scalability的話, clustering 是最常用的手段. 這一篇老文章: Under the Hood of J2EE Clustering, 把一些application server 怎麼做clustering的方法寫得很清楚. 在這邊我要介紹不用tomcat的cluster 機制去做到session replication. 我先複製了兩份tomcat 6.0 的目錄, 分別放在 /opt/server1 和/opt/server2 , 然後分別listen 8080, 8081 port. 然後jvmRoute 分別設成jvm1, jvm2以資區別. (以上如果不知道要怎麼做的話…ㄟㄟ…請看tomcat document) 用port的好處就是可以不用switch, 就把browser 裡的cookie 帶到servers, 因為cookie是只看domain和path的. 而且我還可以用port 控制我要request到哪一台, 可以說是development 測試最方便的作法了.

tomcats 好了之後, 在eclipse 裡開個新web project, 我把他叫tc (名字我都喜歡短的). 就做一個簡單的計數器, 每request 一次就把request過的次數加1, 然後印到畫面上:

CountServlet.java

package com.hanklin;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class CountServlet extends HttpServlet {
    private static final long serialVersionUID = -4951220189098528823L;

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HttpSession session = req.getSession();
        AtomicInteger count = (AtomicInteger) session.getAttribute("count");
        if (count == null) {
            count = new AtomicInteger();
            session.setAttribute("count", count);
        }
        resp.getWriter().write("

" + count.addAndGet(1) + "

"); } }

好吧, 這在使用者第一次request時, 如果同時發很多requests可能會錯, 不過那不是這裡的重點, 所以不管他. 接下來是web.xml, 也很簡單的.

web.xml



  tc
  
    count
    com.hanklin.CountServlet
  
  
    count
    /count
  

然後可以輸出成tc.war檔了, 把tc.war 丟到兩個tomcat 的webapps目錄下面, 然後開server.

$ sudo /opt/server1/startup.sh
$ sudo /opt/server2/startup.sh

依次開兩個chrome來看看:

http://localhost:8080/tc/count http://localhost:8081/tc/count

好的, 你沒見過我, 我也不認識你. 現在該terracotta 大軍 登場了.

terracotta 簡介

Terracotta, 說是簡介可是實在不知道如何”簡”起啊! 以前terracotta網站自己介紹是”Network-attached memory”, 就是要你把他想成NAS(Network-attached storage). 還用很多flash動畫說明, 看起來就像很多java processes 可以合在一起, 形成一個超大的java process. 在這個超大java process 裡面, 被shared的objects 可以被不同的小java processes 存取, 而且會符合Java language的規範, 像是==, hashCode() 會有正確的行為. 而且也會符合Java Memory Model的行為, 如synchronized. 在The Definitive Guide to Terracotta 一書裡面, 是這樣定義Terracotta: “a transparent clustering service for Java applications”. (內心OS: 這本書快出第二版了, 要記得去買) 所以terracotta的架構就是要有一台terracotta server(技術文件叫L2), 管理這些shared objects, 然後有許多的terracotta clients(技術文件叫L1), 也就是你的java applications, 連到terracotta server. 當你的一個java application 寫入一個shared objects時, 另一個java application 可以讀到更新後的值. 感覺起來和一些caching solution很像, 可是如果你有注意到我前面說的, 像是 synchronized 也可以正常運作的話, 就可以知道這並不只是單純的caching objects, 還包含協調行為, 而且Java 的code 是可以不用改的. 不過有一些classes是不能shared, terracotta 把它叫作Non-Portable Classes, 要注意啊. 我最早開始在EC2上弄tomcat clustering 的時候, 發現到在EC2的環境是不支援multicast的. 這樣的話tomcat內建的clustering 機制就無法運作, 只能在load balancer上動手腳, 讓同一個client 始終都導到同一台tomcat上. 這當然不是非常好的辦法, 還好在那時候有找到一篇文章, 說的就是怎麼用terracotta在EC2上架tomcat cluster. 在那之前我就有聽過terracotta, 但是沒有用過, 只覺得terracotta似乎是很有趣. 正好這是個不可多得的好機會來試試terracotta, 雖然terracotta的網站說的比扯鈴還扯, ㄟㄟ…是比傑克還神奇, 什麼true linear scalability, transparent clustering, 但是沒有實際使用過怎麼知道呢? 好的, 以下我們就一步一步, 用terracotta建立tomcat cluster吧!

建立terracotta大軍

萬事起頭難, 先下載Terracotta咩! Terracotta滿討厭的一點就是下載點不公開, 一定要網頁點點點, 它才給你一個很快就失效的URL (其實是放在S3上面, 這個技巧我以後來介紹一下). 而且terracotta的網站改了好多次, 產品名字也換來換去, 連結也換來換去, 搞得我好亂啊! 目前這個下載頁面, 是可以用的啦. 安裝terracotta, 有installer和tar兩種, 我都是用tar 解壓的, 因為只要解壓就裝好了咩. 所以我選”terracotta-3.2.1.tar.gz” 那個下載連結. 下載好以後, 就是安裝步驟:

$ sudo tar zxf terracotta-3.2.1.tar.gz

我是把他丟在/opt 下面, 然後做一個link 叫tc, 所以我的terracotta的安裝目錄就是/opt/tc.

$ sudo ln -s /opt/terracotta-3.2.1 tc

然後要做一個叫作boot jar 的東西:

$ cd tc/bin
$ sudo ./make-boot-jar.sh

2010-03-12 12:13:54,961 INFO - Configuration loaded from the Java resource at '/com/tc/config/schema/setup/default-config.xml', relative to class com.tc.config.schema.setup.StandardXMLFileConfigurationCreator.
2010-03-12 12:14:03,960 INFO - Creating boot JAR at '/opt/terracotta-3.2.1/bin/../lib/dso-boot/dso-boot-hotspot_linux_150_16.jar'...
2010-03-12 12:14:04,360 INFO - Successfully created boot JAR file at '/opt/terracotta-3.2.1/lib/dso-boot/dso-boot-hotspot_linux_160_18.jar'.

要記得這個boot jar 的位置, 等一下會用到. 現在來設定tomcat servers. terracotta在 3.0版本以後有別的方法可以設定, 我還是用以前的作法, 直接在java process 給system properties. 先來編輯$TOMCAT/bin/startup.sh, 修改JAVA_OPTS:

export JAVA_OPTS="-Xbootclasspath/p:/opt/tc/lib/dso-boot/dso-boot-hotspot_linux_160_18.jar -Dtc.install-root=/opt/tc -Dtc.config=localhost:9510 $JAVA_OPTS"

-Xbootclasspath/p: 後面跟的就是boot jar的位置. tc.install-root 就是terracotta的安裝目錄, tc.config是terracotta的設定檔位置, 可以用file path, 但是建議要用ip:port的寫法, 讓每個terracotta client 去找terracotta server 要設定檔.

然後是一些煩人的東西. 如果你有用G1GC, (我就是其中之一…(汗)), 很抱歉, terracotta還不支援G1GC! 移掉這個! -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC

如果你是用 java 1.6.0_18, (我還是其中之一…(大汗)), 要再加上:

-XX:+MustCallLoadClassInternal

這是什麼東西我也不知道, 反正terracotta和java 1.6.0_18 氾沖就對了. 然後就是terracotta的重頭戲了, terracotta的設定檔, 我把他取名tc-config.xml, 放在/opt/tc 下面:

tc-config.xml




  
    
      %(user.home)/terracotta/server-data
      %(user.home)/terracotta/server-logs
    
  

  
    %(user.home)/terracotta/client-logs
    
      
    
  

  
    
      
        tc
      
    
  

主要是分成3塊: , , . 重點是加 tc. 因為我把web application取名tc, 所以要填入tc. 然後因為我用tomcat 6.0, 所以要寫說要用tomat 6.0 的TIM(Terracotta Integration Module). 寫好了之後就下載tim.

$ cd /opt/tc
$ sudo bin/tim-get.sh install-for tc-config.xml

Terracotta 3.2.1, as of 20100302-130324 (Revision 14673 by cruise@su10mo5 from 3.2)

Parsing module: tim-tomcat-6.0:latest
Installing tim-tomcat-6.0 2.1.1 and dependencies...
INSTALLED: tim-tomcat-6.0 2.1.1 - Ok
INSTALLED: tim-tomcat-5.5 2.1.1 - Ok
INSTALLED: tim-tomcat-common 2.1.1 - Ok
SKIPPED: tim-session-common 2.1.1 - Already installed
SKIPPED: tim-distributed-cache 1.3.1 - Already installed
SKIPPED: tim-concurrent-collections 1.3.1 - Already installed

Done.

都OK了, 就可以開terracotta server了!

$ sudo bin/start-tc-server.sh -f /opt/tc/tc-config.xml
2010-03-12 12:53:05,308 INFO - Terracotta 3.2.1, as of 20100302-130324 (Revision 14673 by cruise@su10mo5 from 3.2)
2010-03-12 12:53:06,066 INFO - Configuration loaded from the file at '/opt/tc/tc-config.xml'.
2010-03-12 12:53:06,416 INFO - Log file: '/root/terracotta/server-logs/terracotta-server.log'.
2010-03-12 12:53:09,073 INFO - Available Max Runtime Memory: 496MB
2010-03-12 12:53:16,431 INFO - JMX Server started. Available at URL[service:jmx:jmxmp://0.0.0.0:9520]
2010-03-12 12:53:17,517 INFO - Terracotta Server instance has started up as ACTIVE node on 0:0:0:0:0:0:0:0:9510 successfully, and is now ready for work.

我是每次都用-f去指定設定檔的位置, 以免搞錯. 然後看一下log裡面的configuration file是不是讀到對的. 然後看到ready for work就是開好了. 現在可以開tomcat servers了.

$ sudo /opt/server1/startup.sh
$ sudo /opt/server2/startup.sh

2010-03-12 12:54:20,998 INFO - Terracotta 3.2.1, as of 20100302-130324 (Revision 14673 by cruise@su10mo5 from 3.2)
2010-03-12 12:54:22,306 INFO - Configuration loaded from the server at 'localhost:9510'.
2010-03-12 12:54:23,013 INFO - Log file: '/root/terracotta/client-logs/terracotta-client.log'.
2010-03-12 12:54:30,986 INFO - Connection successfully established to server at 127.0.0.1:9510

可以發現tomcat的開機訊息多了terracotta的東西! 看一下configuration 是不是去terracotta server 讀設定檔. 現在終於到了重要的一刻了, 來試試咩!

一樣, 依次開兩個chrome來看看!

http://localhost:8080/tc/count http://localhost:8081/tc/count

感動! 現在這兩台tomcat 已經合為一體了! (我發誓! 我絕對不是refresh 按兩下去抓圖的!)

開始的結束

用Java 的好處是有很多免費的東西可以用. 有時候太多也是很麻煩, 不知道要用哪一個. 最有名的例子應該就是Java servlet用的MVC framework吧! 我記得至少有40種可以用… 搞到最後大概還是回去用struts, 一方面也是大家用最久最習慣, 一方面也是資源比較多. 後來有一陣Ajax的旋風, 那些MVC framework又馬上就過時了, 因為programming model都不太合. 所以累的地方就是要一直學新的東西咩! 這樣也好啦, 多充實一下自己才不會一下就被人幹掉了. 剛剛我很快的用terracotta, 弄完一個最簡單的tomcat cluster, 但是scalability, HA, performance這些議題是沒完沒了的. 以後我再慢慢把一些經驗寫出來.

Resources