[squid-users] Multiple squids serving one port (was Re: [squid-users] Why single thread?)

From: Dave Dykstra <dwd_at_fnal.gov>
Date: Mon, 06 Oct 2008 16:37:44 -0500

On Mon, Oct 06, 2008 at 01:07:49PM -0700, Gordon Mohr wrote:
> I can't find mention of this '-I' option elsewhere. (It's not in my
> 2.6.STABLE14-based man page.)
>
> Is there a writeup on this option anywhere?
>
> Did it only appear in later versions?

Right, sorry, it appeared in 2.7:
    http://www.squid-cache.org/cgi-bin/cvsweb.cgi/squid/doc/squid.8.in

> Is there a long-name for the option that would be easier to search for?

No.

> I would be interested in seeing your scripts if there are other wrinkles
> to using Squid in this manner. We're currently using squid for
> load-balancing on dedicated dual-core machines, so one core is staying
> completely idle...

I'm including a perl script called 'multisquid' below that uses -I and
assumes that there are '.squid-N.conf' configure scripts where "N" is a
number 0, 1, etc. I'm also including a bash script 'init-squid' that
generates those from a squid.conf based on the number of subdirectories
under the cache_dir of the form 0, 1, etc (up to 4) exist. It makes
squid 0 a cache_peer parent of the others so it's the only one that
makes upstream connections, but they all can serve clients.

- Dave

> Dave Dykstra wrote:
> >Meanwhile the '-I' option to squid makes it possible to run multiple
> >squids serving the same port on the same machine, so you can make use of
> >more CPUs. I've got scripts surrounding squid startups to take
> >advantage of that. Let me know if you're interested in having them.
> >Currently I run a couple machines using 2 squids each on 2 bonded
> >gigabit interfaces in order to get over 200 Mbytes/second throughput.
> >
> >- Dave

------------------------------ multisquid ---------------------------
#!/usr/bin/perl -w
#
# run multiple squids.
# If the command line options are for starting up and listening on a
# socket, first open a socket for them to share with squid -I.
# If either one results in an error exit code, return the first error code.
# Writtten by Dave Dykstra, July 2007
#
use strict;
use Socket;
use IO::Handle;
use Fcntl;

if ($#ARGV < 2) {
  print STDERR "Usage: multisquid squidprefix numsquids http_port [squid_args ...]\n";
  exit 2;
}

my $prefix = shift(@ARGV);
my $numsquids = shift(@ARGV);
my $port = shift(@ARGV);
my $proto = getprotobyname('tcp');

if (!(($#ARGV >= 0) && (($ARGV[0] eq "-k") || ($ARGV[0] eq "-z")))) {
  #open the socket for both squids to listen on if not doing an
  # operation that doesn't use the socket (that is, -k or -z)
  close STDIN;
  my $fd;
  socket($fd, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
  setsockopt($fd, SOL_SOCKET, SO_REUSEADDR, 1) || die "setsockopt: $!";
  bind($fd, sockaddr_in($port, INADDR_ANY)) || die "bind of port $port: $!";
}

my $childn;
for ($childn = 0; $childn < $numsquids; $childn++) {
  if (fork() == 0) {
    exec "$prefix/sbin/squid -f $prefix/etc/.squid-$childn.conf -I @ARGV" || die "exec: $!";
  }
  # get them to start at different times so they're identifiable by squidclient
  sleep 2;
}

my $exitcode = 0;
while(wait() > 0) {
  if (($? != 0) && ($exitcode == 0)) {
    # Take the first non-zero exit code and ignore the other one.
    # exit expects a byte, but the exit code from wait() has signal
    # numbers in low byte and exit code in high byte. Combine them.
    $exitcode = ($? >> 8) | ($? & 255);
  }
}

exit $exitcode;
------------------------------ init-squid ---------------------------
#!/bin/bash
# This script will work with one squid or up to 4 squids on the same http port.
# The number of squids is determined by the existence of cache directories
# as follows. The main path to the cache directories is determined by the
# cache_dir option in squid.conf. To run multiple squids, create directories
# of the form
# `dirname $cache_dir`/$N/`basename $cache_dir`
# where N goes from 0 to the number of squids minus 1. Also create
# cache_log directories of the same form. Note that the cache_log option
# in squid.conf is a file, not a directory, so the $N is up one level:
# cache_log_file=`basename $cache_log`
# cache_log_dir=`dirname $cache_log`
# cache_log_dir=`dirname $cache_log_dir`/$N/`basename $cache_log_dir`
# The access_log should be in the same directory as the cache_log, and
# the pid_filename also needs to be in similarly named directories (the
# same directories as the cache_log is a good choice).

. /etc/init.d/functions

RETVAL=0

INSTALL_DIR=_your_base_install_dir_with_squid_and_utils_subdirectories_
#size at which rotateiflarge will rotate access.log
LARGE_ACCESS_LOG=1000000000

CONF_FILE=$INSTALL_DIR/squid/etc/squid.conf

CACHE_DIR=`awk '$1 == "cache_dir" {x=$3} END{print x}' $CONF_FILE`
CACHE_LOG=`awk '$1 == "cache_log" {x=$2} END{print x}' $CONF_FILE`
ACCESS_LOG=`awk '$1 == "access_log" {x=$2} END{print x}' $CONF_FILE`

squid_dirs()
{
 # if $NUMSQUIDS is 1, echo the parameter, otherwise echo the parameter
 # N times with $N before the basename of the parameter, where N is
 # from 0 to $NUMSQUIDS-1

 if [ $NUMSQUIDS = 1 ]; then
  echo $1
  return
 fi
 typeset N
 N=0
 while [ $N -lt $NUMSQUIDS ]; do
  echo `dirname $1`/$N/`basename $1`
  let N=$N+1
 done
}

#see how many squid cache directories exist (up to 4)
NUMSQUIDS=0
for D in `NUMSQUIDS=4 squid_dirs $CACHE_DIR`; do
 if [ ! -d $D ]; then
  break
 fi
 let NUMSQUIDS=$NUMSQUIDS+1
done

if [ $NUMSQUIDS = 0 ]; then
 NUMSQUIDS=1
 SQUID=$INSTALL_DIR/squid/sbin/squid
 PLURAL=""
else
 PLURAL="s"
 # create the squid.conf files for each squid
 PID_FILENAME=`awk '$1 == "pid_filename" {x=$2} END{print x}' $CONF_FILE`
 HTTP_PORT=`awk '$1 == "http_port" {print $2;exit}' $CONF_FILE`
 SNMP_PORT=`awk '$1 == "snmp_port" {x=$2} END{print x}' $CONF_FILE`
 ICP_PORT=`awk '$1 == "icp_port" {x=$2} END{print x}' $CONF_FILE`
 ICP_PORT=`awk '$1 == "icp_port" {x=$2} END{print x}' $CONF_FILE`
 #visible_hostname is the name that shows up in X-Cache and Via headers
 VISIBLE_HOSTNAME=`awk '$1 == "visible_hostname" {x=$2} END{print x}' $CONF_FILE`
 HOSTNAME=`hostname`
 SQUID="$INSTALL_DIR/utils/sbin/multisquid $INSTALL_DIR/squid $NUMSQUIDS ${HTTP_PORT:-3128}"
 CACHEBASE="$(dirname $CACHE_DIR)"
 LOGBASE="$(dirname $(dirname $CACHE_LOG))"
 PIDBASE="$(dirname $(dirname $PID_FILENAME))"
 N=0
 while [ $N -lt $NUMSQUIDS ]; do
  SEDCMDS="-e \"s,$CACHEBASE/,$CACHEBASE/$N/,\""
  if [ "$LOGBASE" != "$CACHEBASE" ]; then
   SEDCMDS="$SEDCMDS -e \"s,$LOGBASE/,$LOGBASE/$N/,\""
  fi
  if [ "$PIDBASE" != "$CACHEBASE" ] && [ "$PIDBASE" != "$LOGBASE" ]; then
   SEDCMDS="$SEDCMDS -e \"s,$PIDBASE/,$PIDBASE/$N/,\""
  fi
  NEWCONF=$INSTALL_DIR/squid/etc/.squid-$N.conf
  rm -f $NEWCONF
  eval sed $SEDCMDS $INSTALL_DIR/squid/etc/squid.conf | awk '
   BEGIN{print "# DO NOT EDIT -- Automatically generated by '$0'";print}
   /^[ \t#]*http_port[ \t]/ {
    if (!got_http_port) {
     #on first squid, add additional http_port after first one specified,
     # or after first commented http_port if none specified
     portnum=0
     if (substr($0,1,1) != "#") portnum=$2
     else if ("'$HTTP_PORT'" == "") {portnum=3128; print}
     if (portnum != 0) {
      print "#first port overridden by command line -I"
      print "http_port", portnum
      # have each squid also listen on their own port, the first one to
      # be parent of others and the others for forced cache reloads
      print "http_port", portnum - ('$N' + 1)
      got_http_port=1
      next
     }
    }
    else if ((substr($0,1,1) != "#") && ("'$N'" != 0)) {
     # only first squid listens on other http ports, skip this one
     next
    }
   }
   /^[ \t#]*snmp_port[ \t]/ {
    #replace snmp_port with separate number for each squid
    portnum=0
    if (substr($0,1,1) != "#") portnum=$2
    else if ("'$SNMP_PORT'" == "") {portnum=3401; print}
    if (portnum != 0) {
     portnum=portnum + '$N'
     print "snmp_port", portnum
     next
    }
   }
   /^[ \t#]*icp_port[ \t]/ {
    #only first squid listens on icp port
    portnum=0
    if (substr($0,1,1) != "#") portnum=$2
    else if ("'$SNMP_PORT'" == "") {portnum=3130; print}
    if (portnum != 0) {
     if ("'$N'" != 0) portnum=0 # disables the icp port
     print "icp_port", portnum
     next
    }
   }
   /^[ \t#]*cache_peer[ \t]/ {
    #any squid after first one go only to first one as a cache_peer parent
    if ("'$N'" != 0) {
     if (substr($0,1,1) == "#") {
      if (!got_cache_peer) {
       #insert the line after the first commented-out one
       print
       portnum='${HTTP_PORT:-3128}' - 1
       print "cache_peer localhost parent", portnum, "0 no-query"
       got_cache_peer=1
       next
      }
     }
     else {
      #skip any other uncommented cache_peer options on non-first squids
      next
     }
    }
   }
   $1 == "visible_hostname" || /TAG: visible_hostname/ {
    hostname=""
    if (substr($0,1,1) != "#") hostname=$2
    else if ("'$VISIBLE_HOSTNAME'" == "") {hostname="'$HOSTNAME'"; print}
    if (hostname != "") {
     print "visible_hostname",hostname "/'$N'"
    }
   }
   {print}
   ' >$NEWCONF
  chmod a-w $NEWCONF
  let N=$N+1
 done
fi

last_logline_has()
{
  # if first parameter is 1, see if the last line of any of the cache logs
  # of squids contains the other parameters; if the first parameter is 0,
  # see if true for all of the cache logs
  typeset ANY LDIR LFILE
  ANY=$1
  shift
  for LDIR in $(squid_dirs $(dirname $CACHE_LOG)); do
    LFILE=$LDIR/`basename $CACHE_LOG`
    if tail -1 $LFILE | egrep -q "$*"; then
      if [ "$ANY" = 1 ]; then
        # found one match, return true
        return 0
      fi
    else
      if [ "$ANY" = 0 ]; then
        # found one non-match, return false
        return 1
      fi
    fi
  done
  # return true for ANY=0 (all matched), false for ANY=1 (none matched)
  return $ANY
}

start_squid()
{
 echo "Starting $NUMSQUIDS Squid${PLURAL}... "
 $SQUID -DFS
 # it can take a while to check out a large cache

 LIMIT=60 # Number of Tries
 a=1
 while [ $a -le "$LIMIT" ]
 do
  a=$(($a+1))
  sleep 10
  $SQUID -k check
  RETVAL=$?
  if [ $RETVAL != 0 ] ; then
   echo "Squid start failed!!!"
   start_squid_fail
   RETVAL=$?
   break
  else
   if last_logline_has 0 "storeLateRelease: released" ;then
    break
    RETVAL=0
   fi
  fi
 done

  sleep 2
  echo "done."

 [ $RETVAL == 0 ]
}

start_squid_fail()
{
 RETVAL=0
 $SQUID -k shutdown 2>/dev/null|| true # in case all are not shut down
if last_logline_has 1 "(store_errors|faults)" ;then

 echo "Clearing Caches and Restarting $NUMSQUIDS Squid${PLURAL}... "
 for D in `squid_dirs $CACHE_DIR`; do
  rm -rf $D/*
 done
 $SQUID -z
 sleep 3
 $SQUID -DFS
 sleep 3
 $SQUID -k check
 RETVAL=$?
 if [ $RETVAL != 0 ] ; then
  echo "Squid start failed!!!"
  $SQUID -k shutdown 2>/dev/null || true # in case all are not shut down
 fi
else
 echo "Script Can't Fix Squid Problem"
 RETVAL=1
fi
 [ $RETVAL == 0 ]
}

stop_squid()
{
 echo -n "Stopping $NUMSQUIDS Squid${PLURAL}... "
 $SQUID -k shutdown
 RETVAL=$?
 if [ $RETVAL != 0 ] ; then
  echo "Squid stop failed!!!"
 else
  sleep 30
  echo "done."
 fi
 [ $RETVAL == 0 ]
}

cleancache()
{
 echo "Clearing Caches of $NUMSQUIDS Squid${PLURAL}... "
 $SQUID -k shutdown 2>/dev/null||true # in case all are not shut down
 for D in `squid_dirs $CACHE_DIR`; do
  rm -rf $D/*
 done
 $SQUID -z
}

rotate()
{
 echo "Rotating Caches of $NUMSQUIDS Squid${PLURAL}... "
 $SQUID -k rotate
}

rotateiflarge()
{
 for LDIR in $(squid_dirs $(dirname $ACCESS_LOG)); do
  LFILE=$LDIR/`basename $ACCESS_LOG`
  if [ "$(stat -c %s $LFILE)" -gt $LARGE_ACCESS_LOG ]; then
    rotate
    break
  fi
 done
}

start()
{
 start_squid
 [ $RETVAL == 0 ]
}

stop()
{
 stop_squid
 [ $RETVAL == 0 ]
}

case "$1" in
  start)
        cleancache
        start
        ;;
  stop)
        stop
        ;;
  status)
          $SQUID -k check 2>/dev/null
        exit $?
        ;;
  reload)
        $SQUID -k reconfigure
        ;;
  restart)
        stop
        start
        ;;
  cleancache)
       cleancache
       ;;
  rotate)
       rotate
       ;;
  rotateiflarge)
       rotateiflarge
       ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart|reload|cleancache|rotate|rotateiflarge}"
        exit 1
esac
Received on Mon Oct 06 2008 - 21:37:34 MDT

This archive was generated by hypermail 2.2.0 : Tue Oct 07 2008 - 12:00:03 MDT