Um dos novos recursos introduzidos na versão 2.1.0 do SDK do Couchbase .NET foi o suporte para substituir o serializador padrão por seu próprio serializador personalizado, estendendo a interface ITypeSerializer e fornecendo seu próprio serializador ou seu favorito. Neste artigo, mostraremos como fazer isso e forneceremos um exemplo (clonável a partir de couchbase-net-contrib projeto em Couchbaselabs) usando um serializador JSON muito rápido chamado Jil.
Por padrão, o SDK .NET usa o popular serializador NewtonSoft.NET JSON, principalmente porque é um projeto com recursos completos, amplamente usado e com bom suporte. Em geral, o desempenho fornecido pelo NewtonSoft é bom o suficiente para a maioria dos projetos; em alguns casos, porém, você pode querer usar um serializador JSON mais rápido, como o Jil.
O Jil é baseado no Sigil, uma biblioteca de geração de IL à prova de falhas, e fornece um serializador de JSON de uso geral altamente otimizado e que usa vários "truques" para melhorar o desempenho, como evitar alocações (portanto, sem GC) e otimizar a ordem de acesso dos membros para que a CPU não precise ficar parada enquanto aguarda os valores na memória. Você pode ler mais sobre o Jil e o Sigil aqui e aqui.
Etapas necessárias
Estender o serializador padrão é relativamente fácil:
- Primeiro, você fornece sua própria implementação do Couchbase.Core.Serializaton.ITypeSerializer.
- Em seguida, substitua o DefaultSerializer na classe Couchbase.Configuration.Client.ClientConfiguration. É isso aí!
- Opcionalmente, você pode usar o Web.Config ou o App.Config para registrar o novo ITypeSerializer para que o SerializerFactory crie o serializador em tempo de execução.
Implementação do ITypeSerializer
A interface ITypeSerializer é usada pelo transcodificador (ITypeTranscoder) para lidar com a serialização e a desserialização do corpo do pacote do Memcached. O transcodificador usa o Type dos parâmetros genéricos da operação, o OperationCode da operação e os "sinalizadores" codificados no cabeçalho do Memcached para determinar em qual Type o corpo deve ser desserializado e para determinar quais sinalizadores devem ser codificados no pacote durante a serialização.
Embora pareça complexo, tudo é muito simples: se o tipo do corpo for um objeto ou primitivo (int, ulong etc.), o corpo será codificado com um sinalizador JSON e tratado como JSON. Sim, os inteiros são tratados como JSON válido! Se o tipo do corpo for uma string, ele será tratado como uma string e codificado ou decodificado em UTF 8. Por fim, as matrizes de bytes são passadas diretamente e simplesmente anexadas ao pacote ou lidas do pacote e atribuídas ao campo Value do IOperationResult, se a operação tiver um corpo. Portanto, qualquer valor que não seja Strings e matrizes de bytes será serializado ou desserializado usando o ITypeSerializer.
O ITypeSerializer tem as seguintes assinaturas de método que você deve implementar:
1 2 3 4 5 6 7 8 9 10 11 12 |
público interface ITypeSerializer { T Deserializar(byte[] buffer, int compensação, int comprimento); T Deserializar(Fluxo fluxo); byte[] Serializar(objeto obj); } |
Uma sobrecarga para Deserialize recebe um Stream e a outra uma matriz de bytes, offset e comprimento. Serialize recebe apenas uma referência System.Object, que é o valor a ser serializado em uma matriz de bytes para transporte.
A listagem de código do JilSerializer tem a seguinte aparência:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
público classe JilSerializer : ITypeSerializer { ... público T Deserializar(Fluxo fluxo) { usando (var leitor = novo StreamReader(fluxo)) { retorno JSON.Deserializar(leitor, _options); } } público T Deserializar(byte[] buffer, int compensação, int comprimento) { T valor = padrão (T); se (comprimento == 0) retorno valor; usando (var ms = novo Fluxo de memória(buffer, compensação, comprimento)) { usando (var sr = novo StreamReader(ms)) { se (tipo de(T).IsValueType &lificador;&lificador; (!tipo de(T).IsGenericType || tipo de(T).GetGenericTypeDefinition() != tipo de(Nulável<>))) { var tipo = tipo de (Nulável<>).MakeGenericType(tipo de (T)); objeto nullableVal = JSON.Deserializar(sr, tipo, _options); valor = nullableVal == nulo ? padrão(T) : (T)nullableVal; } mais { valor = JSON.Deserializar(sr, _options); } } } retorno valor; } público byte[] Serializar(objeto obj) { usando (var ms = novo Fluxo de memória()) { usando (var sw = novo Gravador de fluxo(ms)) { JSON.Serializar(obj, sw, _options); } retorno ms.ToArray(); } } } |
Observe que o código é praticamente idêntico à lógica do DefaultSerializer, com a exceção de que estamos usando a classe Jil JSON em vez das classes de serialização do Newtonsoft.NET; embora o mecanismo de serialização seja diferente, as regras são as mesmas.
Configuração do SDK para usar o serializador personalizado
Como mencionado anteriormente, o SDK do Couchbase .NET foi atualizado na versão 2.1.0 para permitir serializadores personalizados. Isso é feito substituindo a fábrica de serializadores padrão na ClientConfiguration, que é apenas uma propriedade Func: qualquer coisa que corresponda à assinatura será criada e usada para serialização. Em uma observação lateral, as SerializationSettings e DeserializationSettings foram descontinuadas e não são mais usadas.
Aqui está um exemplo:
1 2 3 4 5 6 7 8 9 10 11 |
var configuração = novo Configuração de cliente { Serializador = () => novo JilSerializer() }; var agrupamento = novo Aglomerado(configuração); var balde = agrupamento.OpenBucket(); |
Embora você possa fornecer uma lógica complexa para a criação do ITypeSerializer, a abordagem mais simples é suficiente.
Se você quiser usar o SerializationFactory para instanciar o serializador, basta fornecer o código de inicialização em seu Web.Config ou App.Config:
1 |
<!--?xml versão="1.0" codificação="utf-8" ?--> |
Para usar a configuração personalizada em seu código, você chamará o CTOR da classe Cluster, que recebe um caminho de cadeia de caracteres que corresponde à sua configuração:
1 2 3 4 |
var agrupamento = novo Aglomerado("couchbaseClients/couchbase"); var balde = agrupamento.OpenBucket(); |
Quando você fizer isso, todos os Buckets abertos a partir do objeto Cluster usarão o JilSerializer em vez do serializador padrão do NewtonSoft.NET.
Conclusão
Com os novos pontos de extensão adicionados ao SDK do Couchbase .NET, é trivial escrever e usar serializadores JSON personalizados usando a interface ITypeSerializer. O serializador JSON do Jil parece impressionante e é fácil de usar; espero que um membro da comunidade crie um para o ServiceStack.Text e o envie para o projeto Couchbase. NET Contrib Project!