Este blog foi publicado originalmente no blog pessoal de Cecile Le Pape&apos. Para ver a postagem original do blog, clique em aqui

 

Em minha post anteriorEm meu artigo, falei sobre como configurar um serviço de gerenciamento de conteúdo flexível usando o Couchbase como repositório de metadados, em cima de um servidor Apache Chemistry. Os blobs em si (pdf, pptx, docx, etc.) são armazenados em um sistema de arquivos separado ou em um armazenamento de blobs. Hoje, gostaria de mostrar como o Couchbase pode ser usado para armazenar os próprios blobs, usando um gerenciador de pedaços personalizado. A ideia é armazenar não apenas os metadados de um documento (data de criação, criador, nome etc.), mas também o próprio blob.

O objetivo dessa nova arquitetura é reduzir o número de sistemas diferentes (e as licenças a serem pagas) e também se beneficiar diretamente dos recursos de replicação oferecidos pelo Couchbase.

Primeiro, vamos lembrar que o Couchbase não é um armazenamento de blob. Trata-se de um armazenamento de documentos baseado em memória, com um gerenciamento de cache adhoc ajustado para que a maioria dos dados armazenados no Couchbase esteja na RAM para consultas rápidas. Os dados também são replicados entre os nós (se a replicação estiver ativada) dentro do cluster e, opcionalmente, fora do cluster, se o XDCR for usado. É por isso que os dados armazenados no Couchbase não podem ser maiores que 20 MB. Esse é um limite rígido e, na vida real, 1 MB já é um documento grande para ser armazenado.

Sabendo disso, a questão é: como posso armazenar dados binários grandes no Couchbase? A resposta é simples: dividindo-os em pedaços!

A nova arquitetura tem a seguinte aparência:

Agora há 2 buckets no Couchbase:

  1. cmismeta : usado para armazenar metadados
  2. cmisstore Usado para armazenar blobs

 

Quando uma pasta é criada, somente o compartimento cmismeta é modificado com uma nova entrada porque, obviamente, uma pasta não está associada a nenhuma bolha. Essa é simplesmente uma estrutura usada pelo usuário para organizar os documentos e navegar na árvore de pastas. As pastas são virtuais. O ponto de entrada da estrutura é a pasta raiz, conforme descrito anteriormente.

Quando um documento (por exemplo, um pdf ou um pptx) é inserido em uma pasta, três coisas acontecem:

  • Um documento json contendo todos os seus metadados é inserido no bucket cmismeta, com uma chave exclusiva. Digamos, por exemplo, que o documento tenha a chave L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=.
  • Um novo documento json com a mesma chave é criado no bucket do cmisstore. Esse documento contém o número de blocos, o tamanho máximo de cada bloco (o mesmo para todos os blocos, exceto para o último, que pode ser menor) e o tipo de mime do aplicativo.
  • A bolha anexada ao documento é dividida em partes binárias (o tamanho depende de um parâmetro que você pode definir nas propriedades do projeto). Por padrão, um pedaço tem 500 KB de tamanho. Cada pedaço é armazenado no bucket do cmisstore como um documento binário, com a mesma chave "L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=" como prefixo e um sufixo "::partxxx", em que xxx é o número do bloco (0, 1, 2, ...).

Por exemplo, se eu inserir um pptx chamado CouchbaseOverview.pptx, cujo tamanho é 4476932 bytes, no Couchbase, obtenho o seguinte:

  • No bucket cmismeta, um documento json chamado L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=

  • No bucket cmisstore, um documento json também chamado L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=

  

  • 9 blocos contendo dados binários e denominados L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=::part0, L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=::part1, ... , L0NvdWNoYmFzZU92ZXJ2aWV3LnBwdHg=::part8

 

O CouchbaseStorageService é a classe que implementa a interface StorageService já usada para armazenamento local ou armazenamento S3, como mostrei em meu blog anterior. A primeira diferença é a reutilização da mesma instância do CouchbaseCluster usada para o MetadataService porque apenas um ambiente do Couchbase deve ser instanciado para economizar muitos recursos (RAM, CPU, rede etc.).

Agora vamos ver o método writeContent propriamente dito:

 

/**
 * O ContentStream é dividido em partes
 */
público vazio writeContent(String dataId, ContentStream fluxo de conteúdo)
lançamentos StorageException { 
        // contar o número de peças 
        longo comprimento = fluxo de conteúdo.getLength();   
        longo nbparts = comprimento / BUFFER_SIZE; 
        // a última parte   
        se (comprimento – nbparts * BUFFER_SIZE > 0)  nbparts++;    
        JsonObject doc = JsonObject.empty(); 
        doc.put("count" (contagem)nbparts); 
        doc.put("mimetype"fluxo de conteúdo.getMimeType()); 
        doc.put("length" (comprimento)comprimento);
        longo totalLength = 0; 
        int ler = 0; // O número de bytes ainda não lidos 
        byte[] byteArray = novo byte[BUFFER_SIZE]; 
        int compensação = 0; 
        para (int i = 0; i < nbpartsi++) { 
            tentar { 
                ler = fluxo de conteúdo.getStream() 
                       .read(byteArray, 0, BUFFER_SIZE);      
                totalLength += ler; 
                             compensação += ler
                writeContentPart(dataId + PARTE_SUFFIX + ibyteArrayler); 
                doc.put(dataId + PARTE_SUFFIX + iler); 
            } captura (IOException e) { 
                 e.printStackTrace();   
            } 
        }
 
        se (totalLength != comprimento) 
                    lançar novo StorageException("Número errado de bytes");      
        
         JsonDocument jsondoc = JsonDocument.create(dataIddoc); 
         balde.upsert(jsondoc); 
     }
 
     privado vazio writeContentPart(String partIdbyte[] bytesArrayint comprimento
          lançamentos StorageException {
                   BinaryDocument bDoc = BinaryDocument.create(partId
          Unpooled.copiedBuffer(bytesArray));
                  balde.upsert(bDoc);
     } 

 

Agora, o que fazer para recuperar o arquivo do Couchbase? A ideia principal é obter cada parte, concatenar cada uma delas na mesma ordem em que foram cortadas e enviar a matriz de bytes para o fluxo. Provavelmente há muitas maneiras de fazer isso, mas eu simplesmente implementei uma maneira direta usando uma única matriz de bytes na qual escrevo cada byte.

 

privado InputStream getInputStream(String dataId, StringBuffer mimeType)
lançamentos StorageException {
JsonDocument 
doc = balde.get(dataId);
JsonObject 
json = doc.content();
Inteiro 
nbparts = json.getInt("count" (contagem));
Inteiro 
comprimento = json.getInt("length" (comprimento));
 
          se(nbparts==nulo || comprimento==nulo || mimeType==nulo
                 lançar novo StorageException("Documento inválido");
          mimeType.append(json.getString("mimetype"));
          byte[] byteArray = novo byte[comprimento]; 
          // para cada parte, leia o conteúdo no byteArray 
          int compensação = 0; 
          Inteiro partLength = nulo; 
         
          para (int i = 0; i < nbpartsi++) {   
               partLength = json.getInt(dataId + PARTE_SUFFIX + i); 
               se(partLength == nulo
                     lançar novo StorageException("comprimento da peça"+i+" é obrigatório");
               BinaryDocument bDoc = 
                    balde.get(dataId + PARTE_SUFFIX + i,BinaryDocument.classe); 
               ByteBuf parte = bDoc.content();
               byte[] dst = novo byte[partLength]; 
               parte.readBytes(dst); 
               para (int k = 0; k < partLengthk++) { 
                    byteArray[k + compensação] = dst[k]; 
               } 
               compensação += partLength; 
               parte.release(); 
          }
          InputStream fluxo = novo ByteArrayInputStream(byteArray);    
          retorno fluxo;
}
 

Por fim, vamos ver o que acontece na ferramenta de bancada fornecida pelo Apache Chemistry? Posso ver o documento na pasta raiz e, se eu clicar duas vezes nele, o conteúdo será transmitido do Couchbase e exibido no visualizador associado (aqui, o PowerPoint) com base no tipo de mime. 

 

Workbench e documento abertos no PowerPoint após um clique duplo 

Autor

Postado por Cecile Le Pape, arquiteta de soluções, Couchbase

Depois de 10 anos na Universidade de Paris 6 (França), onde trabalhou na equipe de banco de dados em projetos de replicação, consistência, XML, p2p, sindicação e dispositivos móveis, Cecile decidiu seguir em frente e começar uma nova experiência de vida no setor. Cecile entrou para a equipe de arquitetos de uma pequena empresa chamada Oceane Consulting para desenvolver projetos de gerenciamento de documentos. Agora ela é Arquiteta de Soluções na Couchbase e está muito feliz por estar envolvida tanto na área técnica quanto em vendas.

Um comentário

  1. Vivek Krishnan abril 1, 2018 em 3:52 am

    Esse é um ótimo artigo, mas gostaria de saber se ele também serve para formatos de vídeo.

Deixar uma resposta