LAMP stack on Glassfish

Posted by Dick on August 26, 2008

Quercus is a cracking library from Caucho , the guys who make the Resin appserver. It lets you run PHP on a servlet engine, which sounds like fun.

  • It’s (about) as fast as mod_php
  • you avoid some of those C-based security nightmares
  • it’s small, self-contained and platform neutral
  • Quercus PHP methods are Java methods, so monitoring/profiling is potentially quite sane
  • you can call into JMS queues etc. from PHP

(Ok, that last one didn’t sound so much like fun).

hello world thing

Quercus ships as a WAR file , you just stick your own PHP into it and deploy.

mkdir war; cd war
jar xvf ../quercus-3.1.6.war
echo '<?php phpinfo(); ?>' > index.php
jar cvf ../phpinfo.war .
asadmin deploy ../phpinfo.war

http://localhost:8080/phpinfo does about 950 reqs/sec .

live fire test

It’d be a bit more realistic to try a full, database backed app.

My conversational PHP is no better than my Java. Fortunately, there are around 870 million pimply teenagers who can code against a LAMP stack, so I have plenty of demo apps to choose from.

I’ve gone for Wordpress because I’ve been running it around the place for years.

create a database and connection pool

(Well, I say ‘database’, but I actually mean MySQL since that’s all Wordpress supports. If you already have a MySQL connection pool on your appserver, skip to the ‘build a war’ bit below).

I’ve managed to avoid MySQL so far, so I had to run the (very nice, 64-bit, 5.1) OSX installer .


 export PATH=$PATH:/usr/local/mysql/bin
  mysql_secure_installation # set a root password
  # create a database and user.
  mysqladmin -u root -p create wordpress
  echo "grant all on wordpress.* to 'wordpress'@'localhost' identified by 'wordpress'; " | mysql -u root -p

Add the mysql JDBC driver to $GLASSFISH-HOME/lib, then bounce the appserver.

cp mysql-connector-java-5.1.6-bin.jar /Applications/NetBeans/glassfish-v2ur2/lib/
asadmin stop-domain;asadmin start-domain

Create the connection pool:

asadmin create-jdbc-connection-pool  --datasourceclassname \
com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource \
 --restype javax.sql.DataSource --property \
 User=wordpress:Password=wordpress:URL=jdbc\\:mysql\\://localhost/wordpress\
 wordpress_pool

check it works:

asadmin ping-connection-pool wordpress_pool
Command ping-connection-pool executed successfully.

Finally, make a JNDI reference to it:

asadmin create-jdbc-resource --connectionpoolid=wordpress_pool jdbc/wordpress

build a war to deploy

Get wordpress and quercus tarballs/WARs.

tar zxvf wordpress-2.6.1.tgz # makes a wordpress/ folder
cd wordpress
unzip ../quercus-1.3.6.war # DON'T replace index.php when prompted.
#edit WEB-INF/web.xml and uncomment these lines
   
database
jdbc/wordpress
   
jar cvf ../awesomeblog.war .
asadmin deploy ../awesomeblog.war

Then browse to http://localhost:8080/awesomeblog. It’ll prompt for missing wp-config.php, but
it doesn’t matter what you specify all DB connections are managed by the jdbc/wordpress pool.

Filled out a few test posts, and it manages a good 50 requests/sec without any tuning.

alternatives

It’s pretty simple to enable appserver wide PHP support if you fancy that. Personally I’d rather have standalone WAR files, but YMMV.

Homework assignments:

  • to cluster this, you’ll need a shared directory for file uploads.
  • mass hosting is going to need one WARfile per blog
  • set table_prefix for each blog (so they can share a connection pool)
  • roll up the deployment in an ant / rake task

decent postgreSQL install on OSX

Posted by Dick on August 17, 2008

public service announcement

I need a good database to run locally. When I Google for ‘postgreSQL OSX’ I see a lot of old posts that start with ‘download and build readline’. No wonder people still use MySQL.

If you’re on a Mac use EnterpriseDBs installer. It’s up to date and you get a reasonably sane default config

(everything is under /Library/PostgreSQL/8.3 if you want to tweak it).

Also bundles docs and pgAdmin (which seems to have become a half-decent tool  while I wasn’t looking) in /Applications/PostgreSQL (I found a JDBC driver under /Library/Extensions/Java too, although that could have come from anywhere).

There are also installers there for Linux (but you might as well use your package manager ) and Windows (no, really).

For production, I’d use something on a ‘real’ UNIX obviously - the Solaris 10 / SXCE bundled PostgreSQL 8.2 / 8.3 works great.

see me rollin, they hatin

Posted by Dick on August 09, 2008

(I have avoided that cliche all year, give me a break)

I’ve run various releases of Roller on various Glassfish versions, and am pretty happy with what I’ve settled on.

Here are some gotchas it’s taken me a while to Google fixes for.

Glassfish v2

Glassfish v3 is going to be awesome when it’s done, but it’s not a production release.
Get the Sun-branded release – it’s the same codebase, but has a pretty installer and the
option of commercial support down the line if you ever need it.

Roller bowler bowler penny a pitch

Roller 3 is obsolete now and clanky to setup. 4.0 is much much easier to deploy (the install guide even has instructions for Glossfish2), but JavaMail only works on 4.0.1. Dave Johnson (snoopdave, Roller developer) made a pre-built Roller 4.0.1 tarball available a while back.

It’s good, you should use it.

friends don’t let friends use DB2

No-one uses DB2 unless they’re forced to (no-one you’d want to talk to, anyway).
Roller hates it even more than I do. Avoid it unless you enjoy pain.

Better options I’ve tried are:

  • PostgreSQL works fine with Roller I love it (but it can be tricky to cluster)
  • MySQL is what most Roller guys use. I can’t bring myself to recommend it (we have previous) but I’d expect less Roller bugs talking to it.
  • we’re currently on an Oracle 10g backend, it works so far. Someone else supports it, so that’s ok with me.
  • Derby/JavaDB worked well enough, but it’s CLI is pretty dreadful (cheers IBM) and you might need to shell into it one day. Good within NetBeans, when you’re too lazy to install a real DB (did I mention the PostgreSQl installer yet)?

fustercluck

Glassfish clusters are easy to setup and actually fun to look after. If you want Glassfish to HA your sessions you should add a <distributable /> element to your web.xml. Roller doesn’t use sessions for much, but it’s not a lot of work, so you might as well.  Other than that, it’s pretty straightforword.
Roller clusters are a different kind of fun.

Out of the box, Roller will do horrible things to you if you try to cluster it.
My favourites (so far)

  • search is broken
  • caching doesn’t work (well, it caches, it jut does it inconsistently on random nodes)
  • scheduled tasks get run twice or not at all, often corrupting your schedule as they do it
  • file uploads need a shared filestore.

I got round these with the following roller-custom.properties:


# hook in an external search engine instead
search.enabled=false

# these should be mounted from a reliable fileserver
uploads.dir=/glassfish/uploads
planet.aggregator.cache.dir=/glassfish/planet

# memcache instead of file-cache, scales better anyway
#
# see my last post for details
#
cache.defaultFactory=net.java.roller.tools.cache.memcached.MemcachedLRUCacheFactoryImpl
cache.memcached.default.servers=clusternode1:11211,clusternode2:11211
# it's a bit verbose by default
log4j.category.net.java.roller.tools.cache.memcached=INFO

# give each node a unique clientId - see below
config.expandedProperties=uploads.dir,search.index.dir,tasks.clientId
tasks.clientId=${roller.nodename}

wait, what?

tasks.clientId needs to be unique for each node in your Roller cluster.
Since we run the same WARfile on each node, we make that an alias to a JVM system property, ‘roller.nodename’, that’s different on each cluster node.

In Glassfish, you can do this by creating a cluster-wide JVM option which is set to a system property, and give each server instance a different value for system property.

Assuming we have a cluster called ‘rollercluster’ with 2 server instances ‘instance01’ and ‘instance02’, this should do the trick:

asadmin create-jvm-options --target roller-cluster '-Droller.nodename=${NODENAME}'
Command create-jvm-options executed successfully.
asadmin create-system-properties --target instance01 NODENAME=instance01
Command create-system-properties executed successfully.
asadmin create-system-properties --target instance02 NODENAME=instance02
Command create-system-properties executed successfully.

Restart your server instances, and
‘ps’ should show ‘-Droller.nodename=instance02’ on the server running instance02.

(Thanks to Anil Gangolli and Kedar Mhaswade on the roller and glassfish lists for helping figure this out)

memcached on solaris 10 for Roller4

Posted by Dick on August 08, 2008

I have a 2-node Glassfishv2 cluster running Roller 4 on Solaris 10
and discovered (the hard way) that memcached is the only safe caching option .

If you’re trying to run Roller on a Glassfish cluster, do yourself a favour and skim over
this checklist. You’ll thank me later, honest.

install memcached

(If you’re running Solaris Express or OpenSolaris, you can skip this bit.)

It’s part of Coolstack now (ignore the page banners, it’s for Solaris Intel as well).
They have a boatload of other things too
(Squid, Ruby, lighttpd and – ooh! – nginx), but I’m trying to stay focussed here.

I pkgadded CSKmemcached_1.3RC1_i386.pkg.bz2 on both nodes.
Bizarrely, the package didn’t include SMF bits, so I tweaked the ones from SXCE.
Help yourself:

curl -O http://files.hellooperator.net/solaris/smf/memcached
cp memcached /lib/svc/method
chmod +x memcached
curl -O http://files.hellooperator.net/solaris/smf/memcached.xml
cp memcached.xml /var/svc/manifest/application/database
svccfg import /var/svc/manifest/application/database/memcached.xml

tweak and enable memcached

I’m giving it 1 Gb of RAM for starters, tweak the “setprop” line below if you want to change it.

bash-3.00# svccfg -s memcached
svc:/application/database/memcached> setprop memcached/options=("-u" "nobody" "-m" "1024")
svc:/application/database/memcached> quit
bash-3.00# svcadm refresh memcached
bash-3.00# svcadm enable memcached

hook up Roller

First, add the roller memcached plugin to your WARfile:

wget https://roller.dev.java.net/files/documents/190/88023/roller-memcached-4.0.tar.gz
gzip -dc roller-memcached-4.0.tar.gz | tar xf -
cp roller-memcached/* $ROLLER_DIR/WEB-INF/lib

Then add this to $ROLLER_DIR/WEB-INF/class/roller-custom.properties


# Rollers caches aren't cluster safe, so use memcache instead
cache.defaultFactory=net.java.roller.tools.cache.memcached.MemcachedLRUCacheFactoryImpl
# any whitespace in this line will waste your afternoon
cache.memcached.default.servers=clusternode1:11211,clusternode2:11211
#debugging
log4j.category.net.java.roller.tools.cache.memcached=DEBUG

The other howtos I’ve seen all specify MemcachedLRUCacheFactory, which gave me ClassNotFoundExceptions (probably because it doesn’t exist?) -
the one above works fine.

Then rebuild your WARfile and redeploy.

Requests/sec has shot up from about 25/sec to over 350/sec, and it can stand a much higher concurrency level now (if you need a quick benchmarking tool, Siege builds well on OSX).
I trashed the webapp and rebuilt it yesterday (swapped out our awful DB2 backend for a proper database) and needed to svcadm restart memcached, but other than that it’s fire-n-forget.

 

(Credits : Dave Johnson is the Roller God, Trond Norbye wrote the SXCE SMF bits)