Sem categoria

libcouchbase - Explore todos os recursos do seu servidor Couchbase a partir de C

Comecei a implementação da libcouchbase há quase um ano, devido à minha necessidade pessoal de testar o funcionamento interno da Couchbase. Como eu trabalho no "núcleo" do Couchase, precisava de uma maneira fácil de testar as alterações que fiz lá. Naquela época, havia apenas clientes para Java, Python e C# com suporte para interface REST para o Couchbase. Não sou fã de Python, e minha máquina Solaris não suporta C#, então Java era minha única opção. Como gosto muito de Java, essa foi minha linguagem de escolha inicialmente quando precisei testar algo. Infelizmente, acho um pouco mais incômodo ter que lidar com várias linguagens (e o fato de não poder vincular o código de teste ao núcleo para testá-lo "por dentro" ;-)) Diante disso, comecei a tentar encontrar uma alternativa.

Nos últimos anos, me esforcei um pouco para libmemcachedPor isso, a primeira coisa que fiz foi ver o que seria necessário para adicionar suporte a um "comportamento de cliente inteligente" na libmemcached. Se isso fosse possível, eu poderia ter uma biblioteca com todos os recursos que as pessoas mantinham. Infelizmente, não foi fácil adicionar o que eu queria devido à forma como a biblioteca funciona. A libmemcached inicializaria sua lista de estruturas de servidor no momento da inicialização e mapearia a chave para o servidor ao qual ela pertenceria. Para o Couchbase, eu teria de poder substituir a lista de servidores a qualquer momento. Além disso, o Couchbase usa um mapeamento da chave em dois níveis. Primeiro, mapeamos a chave para um vbuckete, em seguida, tentamos procurar o servidor onde o bucket reside. Na época, achei que teria de mudar muito o funcionamento interno da libmemcached para que ela funcionasse no Couchbase (e, dado o fato de que a libmemcached não é uma biblioteca específica do fornecedor, achei que era a coisa errada a fazer). Como eu não queria ficar em cima de uma "bomba-relógio" quando se tratava de tentar mesclar meu "patch" privado para a libmemcached com as alterações do upstream, decidi apenas escrever algo que atendesse às minhas necessidades. Acho que vale a pena mencionar que a libcouchbase não foi feita para substituir a libmemcached, mas para ser uma alternativa para aqueles que querem/precisam explorar todos os recursos do Couchbase.

A solução "rápida e suja" teria sido simplesmente codificar as coisas de que eu precisava em vários programas de teste e usar o recurso copiar e colar até encontrar o ceifador, mas isso não teria sido particularmente inovador (ou divertido). Em vez disso, sentei-me e defini alguns critérios para a biblioteca:

  • Tudo isso deve ser feito de forma assíncrona

    Acredito que todas as bibliotecas devem fornecer uma interface assíncrona e, se a biblioteca fornecer uma interface síncrona, ela deverá ser construída sobre a interface assíncrona e aguardar a conclusão.

  • Sem travamento interno

    O uso de bloqueios internamente pode levar à contenção de bloqueios. Se eu puder evitar o bloqueio dentro da biblioteca, ela poderá ser melhor dimensionada. Caberá inteiramente ao cliente da biblioteca proteger as variáveis contra o acesso de vários threads.

  • Tem que ser multiplataforma!

    Faço todo o meu desenvolvimento no Solaris, mas respeito o fato de que outras pessoas podem ter necessidades diferentes das minhas. Algumas pessoas preferem o Mac OS, outras preferem o Windows e já ouvi falar de pessoas que usam o Linux. Por que todas elas não deveriam poder usar a libcouchbase?

  • Não deve depender de um "monte de merda" de outros módulos

    Não é que eu sofra de síndrome de não-inventado-aquiMas acho uma chatice ter que compilar várias outras bibliotecas (que podem ter dependências adicionais etc.) apenas para que a biblioteca funcione (e manter todas elas sincronizadas entre si à medida que surgem novas versões). Todo uso de biblioteca deve ser justificado.

  • Compatibilidade binária

    A interface binária deve ser estável e não mudar o tempo todo. Criar uma boa API não é fácil, portanto, durante o desenvolvimento, espera-se que a API evolua. Se um cliente usar apenas uma interface comprometida, ele não precisará fazer nada além de substituir o objeto compartilhado quando a nova versão for lançada.

  • Sem GPL

    Eu simplesmente não suporto a licença GPL.

Embora eu tenha começado a trabalhar na libcouchbase há um ano, isso não significa que eu tenha trabalhado nela em tempo integral. Meu trabalho principal é trabalhar no núcleo, portanto, só estendi a biblioteca quando precisei da funcionalidade para meus próprios testes. Mais recentemente, fico feliz em dizer que recebi algumas contribuições de algumas pessoas que a pegaram e usaram e de alguns outros desenvolvedores do Couchbase (obrigado Sergey, Paul, Sebastian, Jan e Bill!). Sem dúvida, há algo que podemos fazer melhor, portanto, envie um e-mail para a lista de discussão de desenvolvimento do Couchbase (couchbase@googlegroups.com) se houver algo que você acha que precisa ser consertado. Ainda acho que a biblioteca tem algumas arestas que precisam ser resolvidas antes que você possa começar a usá-la na produção ;-)

Criação do software

Se estiver tentando compilar a libcoucbase no Solaris / *BSD / MacOS ou Linux, basta usar o método "configure && make" com o qual está acostumado. Para os usuários do Windows, escrevi um NMakefile que pode ser usado para compilar e instalar os vários bits. Como estou fazendo quase todo o meu desenvolvimento, posso ter feito coisas "erradas", mas ficarei feliz se você me enviar um e-mail dizendo o que preciso mudar.

Estou usando o Windows 7 com o Microsoft Developer Studio 2010 para criar e testar a biblioteca. Execute os seguintes comandos para criar e instalar os bits:

nmake -f NMakefile install

Por padrão, ele usará c:local como o diretório raiz (para facilitar a criação de um instalador ou movê-lo para onde você quiser). Você sempre pode substituir isso especificando INSTALL da seguinte forma:

nmake -f NMakefile INSTALL=c:couchbase install

Pré-requisitos

Temos alguns pré-requisitos para a libcouchbase. É possível que você encontre pacotes binários disponíveis para sua plataforma, mas não deve ser muito difícil compilar a partir do código-fonte.

1) Os arquivos de cabeçalho da ramificação do mecanismo do memcached.

Basta copiar o diretório memcached de https://github.com/memcached/memcached/tree/engine-pu/include para c:localinclude (ou o que você escolher como diretório)

2) Uma implementação sasl.

A libcouchbase precisa executar autenticações SASL para os diferentes buckets. Se você não quiser instalar uma biblioteca SASL completa, poderá instalar a "libisasl" a partir de: https://github.com/membase/libisasl

3) libvbucket

O mapeamento entre uma chave e o vbucket (e para localizar em qual servidor o vbucket está localizado) é fornecido por essa biblioteca. https://github.com/membase/libvbucket

4) libevent (opcional)

A libcouchbase permite plug-ins para diferentes estruturas de notificação de eventos. A estrutura padrão para sistemas do tipo UNIX é a libevent, portanto, a menos que você crie seu próprio plug-in, convém instalá-la. Observe que o padrão para o Windows é outro, portanto, você não precisa dele para o Windows.

Então, como a biblioteca funciona?

A ideia principal da biblioteca é que tudo deve ser orientado por eventos e que um retorno de chamada deve ser acionado quando algo acontecer. Isso significa que você deve configurar retornos de chamada para lidar com tudo o que quiser. Não existe um simples:

std::string myvalue = libcoucbase->get("hello");

mas você pode implementar isso facilmente, se quiser.

Devido ao fato de não haver bloqueio na libcouchbase, você pode pensar que ela não é adequada para uso em um processo com vários threads, mas isso não é verdade. Desde que você não use o mesmo identificador para a libcouchbase em vários threads, poderá usar quantos threads quiser (se quiser usar a mesma instância da libcouchbase em vários threads, será necessário fornecer bloqueio)

Chega de conversa, mostre-me o código!

Tudo o que você precisa fazer em seu programa para começar a usar a libcouchbase é incluir libcouchbase/couchbase.h. e vincular a libcouchbase. A primeira coisa que você precisa fazer é criar uma instância para a libcouchbase:

#inclui


const char *host = NULL; /* Use localhost:8091 */
const char *username = NULL; /* Nenhum usuário especificado */
const char *password = NULL; /* Nenhuma senha especificada */
const char *bucket = NULL; /* usar o bucket padrão */
struct libcouchbase_io_opt_st *io = NULL; /* Usar opções io padrão */

libcouchbase_t handle = libcouchbase_create(host, nome de usuário, senha,
                                            bucket, io);

Se (handle == NULL) {
   /* Falha ao criar o identificador */
}

O fragmento de código acima não faz nada mais do que alocar o identificador para a libcouchbase e não tentou conectá-la ao servidor para receber a lista de servidores etc. A combinação de nome de usuário/senha aqui será usada para autenticar o servidor REST que escuta na porta do host. Com o identificador instalado, devemos configurar o primeiro retorno de chamada: o manipulador de erros. Vamos criar um callback de erro simples que imprime o erro e encerra o aplicativo:

static void error_callback(libcouchbase_t instance,
                           Erro da libcouchbase_error_t,
                           const char *errinfo)
{
    fprintf(stderr, "%s", libcouchbase_strerror(instance, error));
    Se (errinfo) {
        fprintf(stderr, ": %s", errinfo);
    }
    fprintf(stderr, "n");
    exit(EXIT_FAILURE);
}
A chamada de retorno é instalada com:

libcouchbase_set_error_callback(handle, error_callback);

Agora que temos nossa chamada de retorno de erro instalada, podemos começar a nos conectar ao servidor e receber a lista de servidores. Como tudo é assíncrono, precisamos aguardar a conclusão da conexão (não vou mostrar como usar a biblioteca em um loop de evento compartilhado neste exemplo).

libcouchbase_connect(handle);
// Aguarde a conclusão da conexão
libcouchbase_wait(handle);

Neste momento, temos uma instância "funcional" da libcouchbase que podemos usar. Então, vamos em frente e armazenar alguns itens no cache. Se não nos importarmos com a mensagem de resposta do servidor, não precisaremos configurar uma chamada de retorno, mas, para tornar o exemplo mais completo, vamos criar uma chamada de retorno que encerre o programa se não conseguirmos armazenar um dos itens:

static void storage_callback(libcouchbase_t instance,
                             const void *cookie,
                             libcouchbase_storage_t operation,
                             Erro da libcouchbase_error_t,
                             const void *key, size_t nkey,
                             uint64_t cas)
{
    se (erro != LIBCOUCHBASE_SUCCESS) {
        fprintf(stderr, "Falha ao armazenar "");
        fwrite(key, nkey, 1, stderr);
        fprintf(stderr, ""n");
        exit(EXIT_FAILURE);
    }
}
Não tenho um bom exemplo do que queremos armazenar, então vamos apenas fazer um loop e armazenar alguns números:

libcouchbase_set_storage_callback(instance, storage_callback);
for (int ii = 0; ii < 10; ++ii) {
    char key[80];
    size_t nkey = sprintf(key, "%d", ii);
    libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,
                       key, nkey, &ii, sizeof(ii), 0, 0, 0);
}
/* Aguarde a conclusão de todos eles */
libcouchbase_wait(handle);

Horários

Uma das coisas que considero interessantes na libcouchbase é a capacidade de obter algumas estatísticas de tempo sobre o tráfego atual. Todos os que conhecem o DTrace adoram a possibilidade de obter um histograma que representa o que você decidiu medir. Muitas vezes, quando você está executando suas coisas em produção, pode querer analisar os tempos de resposta obtidos do cluster do Couchbase. Para ajudá-lo a fazer isso, adicionei alguns tempos relativamente leves que você pode usar. Devido à natureza assíncrona da libcouchbase (e ao fato de você ser responsável por conduzir o loop de eventos), é possível que você imponha um grande efeito sobre os tempos, de modo que eles não representem mais a verdade. De qualquer forma, vamos adicionar um exemplo que os utiliza para criar um histograma da seção da loja acima (mas, em vez de executar todos eles em um único lote, use um
conjunto síncrono.

libcouchbase_enable_timings(handle);
for (int ii = 0; ii < 10; ++ii) {
    char key[80];
    size_t nkey = sprintf(key, "%d", ii);
    libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,
                       key, nkey, &ii, sizeof(ii), 0, 0, 0);
    libcouchbase_wait(handle);
}

/* Obter os tempos atuais */
libcouchbase_get_timings(handle, stdout, timings_callback);

/* Parar de coletar informações de tempo */
libcouchbase_disable_timings(handle);

Então, qual é a aparência desse "timings_callback"? Isso depende totalmente de você, mas poderíamos criar um histograma simples com o seguinte código:

static void timings_callback(libcouchbase_t instance, const void *cookie,
                            libcouchbase_timeunit_t timeunit,
                            uint32_t min, uint32_t max,
                            uint32_t total, uint32_t maxtotal)
{
    char buffer[1024];
    int offset = sprintf(buffer, "[%3u - %3u]", min, max);
    switch (timeunit) {
    case LIBCOUCHBASE_TIMEUNIT_NSEC:
        offset += sprintf(buffer + offset, "ns");
        pausa;
    case LIBCOUCHBASE_TIMEUNIT_USEC:
        offset += sprintf(buffer + offset, "us");
        pausa;
    case LIBCOUCHBASE_TIMEUNIT_MSEC:
        offset += sprintf(buffer + offset, "ms");
        pausa;
    case LIBCOUCHBASE_TIMEUNIT_SEC:
        offset += sprintf(buffer + offset, "s");
        pausa;
    padrão:
        ;
    }

    int num = (float)40.0 * (float)total / (float)maxtotal;
    offset += sprintf(buffer + offset, " |");
    for (int ii = 0; ii < num; ++ii) {
        offset += sprintf(buffer + offset, "#");
    }

    offset += sprintf(buffer + offset, " - %un", total);
    fputs(buffer, (FILE*)cookie);
}

Isso geraria algo como:

[140 - 149]us |# - 2
[150 - 159]us |## - 3
[160 - 169]us |######################################## - 47
[170 - 179]us |#################### - 26
[180 - 189]us |########### - 14
[190 - 199]us |# - 2
[210 - 219]us | - 1
[220 - 229]us | - 1
[230 - 239]us | - 1
[250 - 259]us | - 1
[280 - 289]us | - 1
[400 - 409]us | - 1

Código-fonte

Acho que você quer experimentar o programa por conta própria :)

/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#inclui
#inclui
#include



static void error_callback(libcouchbase_t instance,
                           Erro da libcouchbase_error_t,
                           const char *errinfo)
{
   fprintf(stderr, "%s", libcouchbase_strerror(instance, error));
   Se (errinfo) {
      fprintf(stderr, ": %s", errinfo);
   }
   fprintf(stderr, "n");
   exit(EXIT_FAILURE);

}

static void storage_callback(libcouchbase_t instance,
                             const void *cookie,
                             libcouchbase_storage_t operation,
                             Erro da libcouchbase_error_t,
                             const void *key, size_t nkey,
                             uint64_t cas)
{
   se (erro != LIBCOUCHBASE_SUCCESS) {
      fprintf(stderr, "Falha ao armazenar "");
      fwrite(key, nkey, 1, stderr);
      fprintf(stderr, ""n");
      exit(EXIT_FAILURE);
   }
}

static void timings_callback(libcouchbase_t instance, const void *cookie,
                             libcouchbase_timeunit_t timeunit,
                             uint32_t min, uint32_t max,
                             uint32_t total, uint32_t maxtotal)
{
   char buffer[1024];
   int offset = sprintf(buffer, "[%3u - %3u]", min, max);
   switch (timeunit) {
   case LIBCOUCHBASE_TIMEUNIT_NSEC:
      offset += sprintf(buffer + offset, "ns");
      pausa;
   case LIBCOUCHBASE_TIMEUNIT_USEC:
      offset += sprintf(buffer + offset, "us");
      pausa;
   case LIBCOUCHBASE_TIMEUNIT_MSEC:
      offset += sprintf(buffer + offset, "ms");
      pausa;
   case LIBCOUCHBASE_TIMEUNIT_SEC:
      offset += sprintf(buffer + offset, "s");
      pausa;
   padrão:
      ;
   }

   int num = (float)40.0 * (float)total / (float)maxtotal;
   offset += sprintf(buffer + offset, " |");
   for (int ii = 0; ii < num; ++ii) {
      offset += sprintf(buffer + offset, "#");
   }

   offset += sprintf(buffer + offset, " - %un", total);
   fputs(buffer, (FILE*)cookie);
}

int main(int argc, char **argv)
{
   const char *host = NULL; /* Use localhost:8091 */
   const char *username = NULL; /* Nenhum usuário especificado */
   const char *password = NULL; /* Nenhuma senha especificada */
   const char *bucket = NULL; /* usar o bucket padrão */
   struct libcouchbase_io_opt_st *io = NULL; /* Usar opções io padrão */

   libcouchbase_t handle = libcouchbase_create(host, 
                                               nome de usuário, 
                                               senha,
                                               bucket, io);

   Se (handle == NULL) {
      /* Falha ao criar o identificador */
      fprintf(stderr, "Failed to create instancen");
      exit(EXIT_FAILURE);
   }
   libcouchbase_set_error_callback(handle, error_callback);
   libcouchbase_connect(handle);
   // Aguarde a conclusão da conexão
   libcouchbase_wait(handle);
   libcouchbase_set_storage_callback(instance, storage_callback);  
   libcouchbase_enable_timings(handle);
   for (int ii = 0; ii < 100; ++ii) {
      char key[80];
      size_t nkey = sprintf(key, "%d", ii);
      libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,
                         key, nkey, &ii, sizeof(ii), 0, 0, 0);
      libcouchbase_wait(handle);
   }

   /* Obter os tempos atuais */
   libcouchbase_get_timings(handle, stdout, timings_callback);

   /* Parar de coletar informações de tempo */
   libcouchbase_disable_timings(handle);
   libcouchbase_destroy(handle);

   retornar 0;
}

Compartilhe este artigo
Receba atualizações do blog do Couchbase em sua caixa de entrada
Esse campo é obrigatório.

Autor

Postado por Trond Norbye, desenvolvedor sênior, Couchbase

Trond Norbye é arquiteto de software na Couchbase. Principal colaborador dos projetos Couchbase e Memcached. Criou as bibliotecas de clientes C/C++ e node.js do Couchbase.

3 Comentários

  1. Uau, isso é muito bom, sem mencionar a rapidez. Vou ver se consigo colocar um rapper de rubi em cima desse passe de amor aos desenvolvedores de rubi.

  2. Matt Ingenthron outubro 6, 2011 em 4:47 am

    Na verdade, um de nossos funcionários (avsej no github) já está pensando nisso. Você deveria enviar uma mensagem para ele e coordenar os esforços! Ele tem sido muito ativo com a biblioteca de cliente Ruby do Couchbase (também em https://github.com/couchbase/c.... Ele está pensando que isso pode funcionar bem lá.

    Você pode entrar em contato com ele por meio de couchbase@googlegroups.com

  3. Ótima introdução, infelizmente o drupal comeu seus includes :)

Deixe um comentário

Pronto para começar a usar o Couchbase Capella?

Iniciar a construção

Confira nosso portal do desenvolvedor para explorar o NoSQL, procurar recursos e começar a usar os tutoriais.

Use o Capella gratuitamente

Comece a trabalhar com o Couchbase em apenas alguns cliques. O Capella DBaaS é a maneira mais fácil e rápida de começar.

Entre em contato

Deseja saber mais sobre as ofertas do Couchbase? Deixe-nos ajudar.