Home » Development » Multiple memcached instances on one server

Multiple memcached instances on one server

I had to run multiple WordPress sites on one server and for fast loading I’m using memcached plugin and the solution was multiple memcached instances.

Install Memcached  and changes

First install memcached and PHP5 extension

apt-get install memcached php5-memcached

Change the startup script

vi /etc/init.d/memcached
#! /bin/sh
### BEGIN INIT INFO
# Provides:		memcached
# Required-Start:	$syslog
# Required-Stop:	$syslog
# Should-Start:		$local_fs
# Should-Stop:		$local_fs
# Default-Start:	2 3 4 5
# Default-Stop:		0 1 6
# Short-Description:	memcached - Memory caching daemon
# Description:		memcached - Memory caching daemon 
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/memcached
DAEMONNAME=memcached
DAEMONBOOTSTRAP=/usr/share/memcached/scripts/start-memcached
DESC=memcached

test -x $DAEMON || exit 0
test -x $DAEMONBOOTSTRAP || exit 0

set -e

FILES=(/etc/memcached_*.conf);
# check for alternative config schema
if [ -r "${FILES[0]}" ]; then
  CONFIGS=();
  for FILE in "${FILES[@]}";
  do
    # remove prefix
    NAME=${FILE#/etc/};
    # remove suffix
    NAME=${NAME%.conf};

    # check optional second param
    if [ $# -ne 2 ];
    then
      # add to config array
      CONFIGS+=($NAME);
    elif [ "memcached_$2" == "$NAME" ];
    then
      # use only one memcached
      CONFIGS=($NAME);
      break;
    fi;
  done;

  if [ ${#CONFIGS[@]} == 0 ];
  then
    echo "Config not exist for: $2" >&2;
    exit 1;
  fi;
else
  CONFIGS=(memcached);
fi;

CONFIG_NUM=${#CONFIGS[@]};
for ((i=0; i < $CONFIG_NUM; i++)); do
  NAME=${CONFIGS[${i}]};
  PIDFILE="/var/run/${NAME}.pid";

case "$1" in
  start)
	echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE
	echo "$NAME."
	;;
  stop)
	echo -n "Stopping $DESC: "
	start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON 
	echo "$NAME."
	rm -f $PIDFILE
	;;

  restart|force-reload)
	#
	#	If the "reload" option is implemented, move the "force-reload"
	#	option to the "reload" entry above. If not, "force-reload" is
	#	just the same as "restart".
	#
	echo -n "Restarting $DESC: "
	start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
	rm -f $PIDFILE
	sleep 1
        start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE
	echo "$NAME."
	;;
  *)
	N=/etc/init.d/$NAME
	# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
	echo "Usage: $N {start|stop|restart|force-reload}" >&2
	exit 1
	;;
esac
done;

exit 0

Change the start file

vi /usr/share/memcached/scripts/start-memcached
#!/usr/bin/perl -w

# start-memcached
# 2003/2004 - Jay Bonci 
# This script handles the parsing of the /etc/memcached.conf file
# and was originally created for the Debian distribution.
# Anyone may use this little script under the same terms as
# memcached itself.

use strict;

if($> != 0 and $< != 0)
{
	print STDERR "Only root wants to run start-memcached.\n";
	exit;
}

my $params; my $etchandle; my $etcfile = "/etc/memcached.conf";

# This script assumes that memcached is located at /usr/bin/memcached, and
# that the pidfile is writable at /var/run/memcached.pid

my $memcached = "/usr/bin/memcached";
my $pidfile = "/var/run/memcached.pid";

if (scalar(@ARGV) == 2) {
	$etcfile = shift(@ARGV);
	$pidfile = shift(@ARGV);
}

# If we don't get a valid logfile parameter in the /etc/memcached.conf file,
# we'll just throw away all of our in-daemon output. We need to re-tie it so
# that non-bash shells will not hang on logout. Thanks to Michael Renner for 
# the tip
my $fd_reopened = "/dev/null";

	sub handle_logfile
	{
		my ($logfile) = @_;
		$fd_reopened = $logfile;
	}

	sub reopen_logfile
	{
		my ($logfile) = @_;

		open *STDERR, ">>$logfile";
		open *STDOUT, ">>$logfile";
		open *STDIN, ">>/dev/null";
		$fd_reopened = $logfile;
	}

# This is set up in place here to support other non -[a-z] directives

my $conf_directives = {
	"logfile" => \&handle_logfile,
};

if(open $etchandle, $etcfile)
{
	foreach my $line (< $etchandle>)
	{
		$line ||= "";
		$line =~ s/\#.*//g;
		$line =~ s/\s+$//g;
		$line =~ s/^\s+//g;
		next unless $line;
		next if $line =~ /^\-[dh]/;

		if($line =~ /^[^\-]/)
		{
			my ($directive, $arg) = $line =~ /^(.*?)\s+(.*)/; 
			$conf_directives->{$directive}->($arg);
			next;
		}

		push @$params, $line;		
	}

}else{
	$params = [];
}

	push @$params, "-u root" unless(grep "-u", @$params);
	$params = join " ", @$params;

if(-e $pidfile)
{
	open PIDHANDLE, "$pidfile";
	my $localpid = <PIDHANDLE>;
	close PIDHANDLE;

	chomp $localpid;
	if(-d "/proc/$localpid")
	{
		print STDERR "memcached is already running.\n"; 
		exit;		
	}else{
		`rm -f $localpid`;
	}

}

my $pid = fork();

if($pid == 0)
{
		reopen_logfile($fd_reopened);
		exec "$memcached $params";
		exit(0);

}else{
	if(open PIDHANDLE,">$pidfile")
	{
		print PIDHANDLE $pid;
		close PIDHANDLE;
	}else{

		print STDERR "Can't write pidfile to $pidfile.\n";
	}
}

Setup multiple config files

The following modifications will search for config files with the following pattern /etc/memcached_*.conf, so if you have 2 websites you can copy memcached.conf file and change the instance port.

cp /etc/memcached.conf /etc/memcached_website1.conf
cp /etc/memcached.conf /etc/memcached_website2.conf

Now you can start all servers at once with:

/etc/init.d/memcached start

Or add website1 or website2 at the end to start/stop/restart specific memcached instance

/etc/init.d/memcached restart website2

Original patches: https://gist.github.com/jonhiggs/516746

About Nikola Stojanoski

System Administrator and Developer. Giving back to the community by blogging about my problems, solutions and practical howto's.
  • Thomas

    Hi,

    The first script shell has an incorrect shell interpreter (it’s /bin/bash instead of /bin/sh)

    and there’s an error in the second script perl :
    my $localpid = ;

    Once I remove the ‘=’ which I guess shouldn’t be there I get
    an error saying $directive is not initialized :
    $conf_directives->{$directive}->($arg);

    [00:29] root@mos-blv-webdev00:/etc# /etc/init.d/memcached start
    Starting memcached: Use of uninitialized value $directive in hash element at /usr/share/memcached/scripts/start-memcached line 73.
    Use of uninitialized value in subroutine entry at /usr/share/memcached/scripts/start-memcached line 73.
    Can’t use string (“”) as a subroutine ref while “strict refs” in use at /usr/share/memcached/scripts/start-memcached line 73.
    [00:29] root@mos-blv-webdev00:/etc#

    any idea ?

  • Thomas

    Ok,
    comparing with the orignal scripts I found that :
    my $localpid = ;

    should be :

    my $localpid = <PIDHANDLE>;

    and

    foreach my $line (< $etchandle>)
    has an additionnal space after <

    [01:07] root@mos-blv-webdev00:/etc# /etc/init.d/memcached start
    Starting memcached: memcached_website1.
    Starting memcached: memcached_website2.
    [01:07] root@mos-blv-webdev00:/etc# ps -edf | grep memcached
    nobody 4831 1 0 00:11 ? 00:00:00 /usr/bin/memcached -m 64 -p 11211 -u nobody -l 127.0.0.1
    thomas 7491 7471 0 01:02 pts/4 00:00:00 vi /usr/share/memcached/scripts/start-memcached_ori
    nobody 7747 1 0 01:07 pts/1 00:00:00 /usr/bin/memcached -m 64 -p 11212 -u nobody -l 127.0.0.1
    root 7761 4687 0 01:08 pts/1 00:00:00 grep memcached
    [01:08] root@mos-blv-webdev00:/etc#

    it’s working !

    • nstojanoski

      THe html mixed my code, thanks i’ve fixed it.

      • Thomas

        Thanks, I did figure out the error myself in the meantime.
        It’s quite useful, many thanks for sharing !

  • Thomas

    also, in the config file, you must modify the path of the log file in addition to modifying the port.

    And note that right after the apt-get install, memcached is started on port 11211, so before issuing a start with multiple instances, kill the initial instance.