Roberto Moutinho

Custom Log Format no varnishncsa

by on Apr.26, 2012, under Linux, Varnish

Para quem precisa definir um padrão diferente do “apache commons log” para servidores Varnish… esta tarefa trivial esconde um problema chato de solucionar.

De acordo com a documentação (varnishncsa), o parametro -F serve exatamente para este proposito. Mas nem tudo são flores, pois ao tentar declarar este padrão direto ao DAEMON_OPTS do init script (/etc/init.d/varnishncsa – redhat/centos) o processo silenciosamente entra em modo “default” e segue sem erros…

O problema mora nas aspas que precisam ser “escaped” corretamente (+ info) e acabam por se misturar com as aspas do atributo.

A forma mais simples de contornar este problema é trabalhar com mais um atributo e adiciona-lo na execução, conforme o script abaixo:

#! /bin/sh
#
# varnishncsa Control the Varnish NCSA logging daemon
#
# chkconfig: - 90 10
# description: Varnish Cache logging daemon
# processname: varnishncsa
# config: 
# pidfile: /var/run/varnishncsa.pid

### BEGIN INIT INFO
# Provides: varnishncsa
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start:
# Default-Stop:
# Short-Description: start and stop varnishncsa
# Description: Varnish Cache NCSA logging daemon
### END INIT INFO

# Source function library.
. /etc/init.d/functions

retval=0
pidfile="/var/run/varnishncsa.pid"
lockfile="/var/lock/subsys/varnishncsa"
logfile="/var/log/varnish/varnishncsa.log"

exec="/usr/bin/varnishncsa"
prog="varnishncsa"

LOG_FORMAT="-F \"\"%{X-IP}i\" %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\"\""
DAEMON_OPTS="-c -a -w $logfile -P $pidfile -D"

# Include varnishncsa defaults
[ -e /etc/sysconfig/varnishncsa ] && . /etc/sysconfig/varnishncsa

start() {

	if [ ! -x $exec ]
	then
		echo $exec not found
		exit 5
	fi

	echo -n "Starting varnish ncsa logging daemon: "

	daemon --pidfile $pidfile $exec "$DAEMON_OPTS" "$LOG_FORMAT"
	echo
	return $retval
}

stop() {
	echo -n "Stopping varnish ncsa logging daemon: "
	killproc -p $pidfile $prog
	retval=$?
	echo
	[ $retval -eq 0 ] && rm -f $lockfile
	return $retval
}

restart() {
	stop
	start
}

reload() {
	restart
}

force_reload() {
	restart
}

rh_status() {
	status -p $pidfile $prog
}

rh_status_q() {
	rh_status >/dev/null 2>&1
}

# See how we were called.
case "$1" in
	start)
		rh_status_q && exit 0
		$1
		;;
	stop)
		rh_status_q || exit 0
		$1
		;;
	restart)
		$1
		;;
	reload)
		rh_status_q || exit 7
		$1
		;;
	force-reload)
		force_reload
		;;
	status)
		rh_status
		;;
	condrestart|try-restart)
		rh_status_q || exit 0
		restart
		;;
	*)
	echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"

	exit 2
esac

exit $?

Leave a Comment more...

Checar se processo está rodando – Shell script para processos 24/7

by on Apr.25, 2012, under Linux

Para processos criticos 24/7, que inexplicavelmente (ou explicavelmente) tem o habito de terminarem sua execução, segue um pequeno shell script para ajudá-los na tarefa de mantê-los no ar e evitar ter de trabalhar via VPN 3:00am.

#!/bin/bash

cd /directory/where/your/process/is

process='com.yourdomain.yourProcess'


if [ "$1" == 'stop' ]
        then
                ps -ef | grep -v grep | grep $process
                if [ $? -eq 1 ]
                        then
                                echo "yourProcess [STOP] - No process found"
                        else
                                kill -9 `ps -ef | awk '/[c]om.yourdomain.yourProcess/{print $2}'`
                                echo "yourProcess [STOP] - Stopped"
                fi
        else
                ps -ef | grep -v grep | grep $process
                if [ $? -eq 1 ]
                        then
                                java -cp .:../lib/mysql-connector-java-5.1.16.jar com.yourdomain.yourProcess & > /dev/null
                                echo $$ > yourProcess.pid
                                echo "yourProcess [START] - Process started. Check logs for runtime problems"
                        else
                                echo "yourProcess [WARNING] - Process already running"
                fi
fi

 

Para isso… basta colocar este processo no cron para verificar de 5 em 5 minutos se o processo ainda está sendo executado.

Leave a Comment more...

Modificando SQL com parametros de indexação do Solr (DIH)

by on Apr.24, 2012, under MySQL, Solr

Uma grande sacada na utilização do DataImportHandler (tirada do livro “Apache Solr 3 Enterprise Search Server“) é a possibilidade de passar parametros para o SQL que está contido no arquivo de configuração.

Um bom caso de uso é a possibilidade de informar via SQL se a extração de dados deve ser completa ou parcial (delta).

vamos ao exemplo:

<dataConfig>
        <dataSource
                type="JdbcDataSource"
                name="ds"
                driver="com.mysql.jdbc.Driver"
                url="jdbc:mysql://127.0.0.1/my_db"
                encoding="UTF-8"
                user="usuario"
                password="senha"
		batchSize="-1" // usar somente com MySQL
        />

	<document name="product">
		<entity name="item" 
			dataSource="ds"
			pk="key"  
			query=" 
select * from my_db.my_table 
where ('${dataimporter.request.fullindex}' != 'false'
or
timestamp &gt; date_sub(now(),interval 30 minute))
">
		</entity> 
	</document> 
</dataConfig>

Desta forma, o parametro “${dataimporter.request.fullindex}” será substituido pelo valor passado na requisição.

http://127.0.0.1/solr/myCore/dataimport?command=full-import&&fullindex=true

fará com que o SQL final seja interpretado como uma extração total (sem o parametro que define onde o timestamp deverá ser maior do que os ultimos 5 minutos)

http://127.0.0.1/solr/myCore/dataimport?command=full-import&&fullindex=false

fará com que o SQL final seja interpretado como uma extração parcial. (mesmo com o “command=full-import”)

Leave a Comment more...

Grails 2.0 com MongoDB – Connection refused

by on Apr.23, 2012, under Grails, MongoDB

Se você também está enfrentando problemas de recusa de conexão do MongoDB para sua app Grails (seja 2.0 ou não)…
Antes de começar a chutar seu desktop/notebook, verifique a porta de conexão configurada em seu DataSource.groovy:

Está escrito “port = 27107” certo ?

Pois bem… a documentação do plugin está errada (até a presente data).

A porta correta é 27017 e não a informada na documentação.

Este erro de documentação passa facilmente despercebido na hora de configurar e da uma certa dor de cabeça…

Comments Off :, , more...

Escrevendo um transformador dih para Solr

by on Apr.20, 2012, under Solr

O primeiro passo de quem está desenvolvendo/configurando o Solr, geralmente é transformar um dado utilizando javascript declarado dentro do proprio arquivo de configuração… porém por experiência propria, a manutenção deste codigo se torna complexa (por falta de um bom debug) e é especialmente lento no processo de indexação.

Eis então que surge o transformador em formato de “lib” importada para o DIH (DIHCustomTransformer).

Se você já sabe que todos os seus documentos sofrerão algum tipo de transformação de dados, você já pode salvar algum tempo e declarar um transformador mais genérico (sem necessidade de declarar quais atributos sofrerão customização). Como por exemplo, um website aonde as url são construidas com algumas tecnicas de SEO combinadas e podem sofrer alteração durante sua edição (tornando uma persistência desta uri em banco uma opção ruim).

Chega de blábláblá…

Sua classe deverá implementar obrigatoriamente um metodo chamado “transformRow”. [ public Object transformRow(Map<String, Object> row) ]

private static final String IN_TAG = "rawTags";
private static final String TAG = "tags";

public Object transformRow(Map<String, Object> row) {

// Obter o valor da coluna retornada do DB.
		String tag = (String) row.get(IN_TAG);

		if (null != tag) {

			String[] tagList = tag.split(",");

// adiciona como campo multi-valor as tags já "splitadas" por virgula.
			row.put(TAG,tagList);

// remove o atributo nao tratado do documento a ser indexado. (basta nao declarar em seu schema, mas por boa pratica é interessante remover)
			row.remove(IN_TAG);

		}
}

dificil não ? (LOL)

Apartir deste exemplo básico tudo se torna um jogo e quebra-cabeça.

public class customTransformer {

	private static final String IN_TAG = "rawTags";
	private static final String TAG = "tags";

	private static final String URL = "url";
	private static final String TITLE = "title";
	private static final String DOMAIN = "domain";
	private static final String FOLDER = "folder";

	public Object transformRow(Map<String, Object> row) {

		String title = (String) row.get(TITLE);
		String domain = (String) row.get(DOMAIN);
		String folder = (String) row.get(FOLDER);

			StringBuffer url = new StringBuffer();
			url.append("http://");
			url.append(domain.toLowerCase());
			url.append("/");
			url.append(folder.toLowerCase());
			url.append("/");
			url.append(lowerCaseStripSpacesAndAccents(title));
			url.append(".html");
			String SeoUrl = url.toString();
			row.put(URL, SeoUrl);

		String tag = (String) row.get(IN_TAG);

		if (null != tag) {

			String[] tagList = tag.split(",");
			row.put(TAG,tagList);
			row.remove(IN_TAG);

		}

	}

obs: Antes de escrever um transformador customizado para o Solr é necessário ter bom conhecimento de como funciona o Data Import Handler.

Comments Off more...

Combinando Memcached, MySQL e codigo C para autenticar usuários de uma API RESTFull.

by on Sep.05, 2011, under Varnish

Depois de algum tempo pesquisando (e principalmente inspirado pelo post do Dr. Carter), resolvi implementar uma autenticação de clientes API (RESTfull) que precisava escalar.

O intuito deste post é compartilhar o pedaço de codigo (inline C) hoje responsável pela autenticação dos clientes e não como configurar o Varnish Cache para escalar websites.

O codigo consiste em pegar a chave de autenticação enviada através de um Header HTTP especifico e verificar sua autenticidade, combinando com a contagem de requests que o cliente possui permissão (tudo isso evitando ao maximo uma chamada ao banco de dados).

/* Import these modules in the begining of your VCL code */

C{
#include <string.h>
#include </usr/local/include/libmemcached/memcached.h>
#include <syslog.h>
#include </usr/include/mysql/mysql.h>
#include <stdio.h>
}C

/* Place the code inside your "vcl_recv" wherever you want (as longs as your requests passes thru the code) */

C{
      memcached_server_st *servers = NULL;
      memcached_st *memc;
      memcached_return_t rc;
	/* 
	I use a combination of "API Key + Client IP" to count the requests.
	Adapt the key used to suit your needs. 
	*/
      char key[50];
      strcat(key, VRT_GetHdr(sp, HDR_REQ, "\012X-API-Key:"));
      strcat(key, "_");
      strcat(key, VRT_IP_string(sp, VRT_r_client_ip(sp)));

      memc= memcached_create(NULL);
      servers= memcached_server_list_append(servers, "localhost", 11211, &rc);
      rc= memcached_server_push(memc, servers);
      syslog(LOG_INFO, "Memcached connection = %s",memcached_strerror(memc, rc));
	/* If the connection to memcached was succesfull we'll decrement the value of this key. */
      if (rc == MEMCACHED_SUCCESS) {
	rc = memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 10);
        uint64_t intval;
        rc= memcached_decrement(memc, key, strlen(key), (uint64_t)1, &intval);

        if (rc != MEMCACHED_SUCCESS) {
	/* 
	If decrement has failed (probably the key is not in cache) we need to authenticate the key on database.
	Remember to create a view (for safety reasons the mysql user is only privileged to use the created view) and to add an index on your table (for a fast read).
	*/
          syslog(LOG_INFO, "Key found in Memcached ? = %s",memcached_strerror(memc, rc));
	  MYSQL     *conn;
	  MYSQL_RES *res;
	  MYSQL_ROW  row;
	  char rows;
	  char query[200];
	  sprintf(query, "select request-limit from view where api_key = '%s'" ,VRT_GetHdr(sp, HDR_REQ, "\012X-API-Key:"));
	  conn = mysql_init(NULL);
	  if (mysql_real_connect (conn,"localhost","user","password","database_name",0,NULL,0) != NULL){
	    mysql_real_query(conn,query,strlen(query));
	    res = mysql_store_result(conn);
	    row = mysql_fetch_row(res);

	    if (row) {
		  	syslog(LOG_INFO, "Result != NULL");
			char *limit = row[0];
			syslog(LOG_INFO,"Key found in MySQL with %s requests total.",limit);
			rc= memcached_set(memc, key, strlen(key), limit, strlen(limit), (time_t)3600, (uint32_t)0);
			syslog(LOG_INFO, "Key %s stored in Memcached ? = %s",key,memcached_strerror(memc, rc));
			/*
			If the API Key was found in database we collect the request limit value and store it.
			I also set a 1 hour limit for this memcached object (3600) and log the response code from memcached.
			*/
	    } else {
 			 syslog(LOG_INFO, "Key: %s not found in MySQL.",VRT_GetHdr(sp, HDR_REQ, "\012X-API-Key:"));
	                 VRT_error(sp, 401, "Unauthorized.");
        	         VRT_done(sp, VCL_RET_ERROR);
			/* The provided key was not found in database. Return a "401 Unauthorized" code. */
	    }
          } else {
		    syslog(LOG_INFO,"MySQL: Error %u (%s)",mysql_errno (conn), mysql_error (conn));
                    VRT_error(sp, 500, "Internal server error.");
                    VRT_done(sp, VCL_RET_ERROR);
			/* In case of a database connection problem we return a "500 Internal server error" code. */
          }
    	  mysql_free_result(res);
	  mysql_close(conn);
        }
        else {
	  syslog(LOG_INFO, "Key found in Memcached ? = %s",memcached_strerror(memc, rc));
          syslog(LOG_INFO, "Decrement key %s in Memcached ? = %s",key,memcached_strerror(memc, rc));

          if (intval < 1) {
	    syslog(LOG_INFO, "Key %s limit exceeded. Client Ip = %s",VRT_GetHdr(sp, HDR_REQ, "\012X-API-Key:"),VRT_IP_string(sp, VRT_r_client_ip(sp)));
            VRT_error(sp, 403, "Forbidden");
            VRT_done(sp, VCL_RET_ERROR);
		/* If the decremented result from memcached is lower than "1" we return a "403 Forbidden" code. Client requests maxed-out. */
          }
        }
      }
      memcached_free(memc);
      memcached_server_list_free(servers);
}C

para o carregamento das bibliotecas necessárias, você precisará adicionar ao script de inicialização do Varnish (/etc/sysconfig/varnish) os seguintes parametros:

-p ‘cc_command=exec cc -fpic -shared -Wl,-x -L/usr/local/include/libmemcached/memcached.h -L/usr/lib64/mysql -lmysqlclient -lmemcached -o %o %s’

 

obs1: você vai precisar ter instalado em seu servidor o mysql-client & mysql-devel package assim como a libmemcached.

A thank you note to the varnish mailing list which assisted me thru the entire process.

Leave a Comment :, , , , , , , , more...

Procurando por alguma coisa ?

Archives

All entries, chronologically...