zoukankan      html  css  js  c++  java
  • dotnetcore 与 hbase 之三——c# for hbase 客户端的使用

    说明

    在上一篇文章dotnetcore 与 hbase 之二——thrift 客户端的制作中已经可以找到 c# hbase 客户端的使用方法了,为什么这里单独列出一篇文章来讲述呢?最简单的理由就是,本篇将为客户端的使用讲述一些简化性工作以及需要注意的事项。为此,我们做了一些工作HbaseNetCore

    存在的差异

    在 c# hbase 客户端中,接口基本只接受或返回 byte 数组型的参数,比如:

      Task<List<byte[]>> getTableNamesAsync(CancellationToken cancellationToken); # 获取所有hbase中的表名
      Task<List<TRowResult>> getRowAsync(byte[] tableName, byte[] row, Dictionary<byte[], byte[]> attributes, CancellationToken cancellationToken); # 根据rowkey获取一行数据
    

    所以,大多数的工作将是 customer's class 与 byte array 之间的转换。需要注意的事项正是来源于 c#与 java 的一些差异。

    1. byte 数组大小端

    在我的工作中遇到的 java 主要使用类org.apache.hadoop.hbase.util.Bytes来进行二进制化(例如:Bytes.toBytes(/**/)),以此来达到和 hbase 进行数据交换的目的。在 Java 中 byte 数组与网络字节流采取一致的大小端模式——大端模式。而在 c#中,主要使用类System.BitConverter来获取二进制流,得到的 bytes 结果是小端模式的。因此,为了通用,我们需要使用Array.Reverse()方法来进行大小端反转。也可以使用System.Net.IPAddress.HostToNetworkOrder()来直接获取网络字节顺序的 bytes。但是其结果不是 byte[],仍然需要使用System.BitConverter来获取 byte[]。此外,单字节编码的字符串(比如:ASCII 、utf-8)不存在大小端的问题,双字节及以上需要注意大小端(比如:utf-16、GB2312)。

    2. 基本值类型的对应

    在 Java 中,基本数据类型(byte、short、int、long、float、double、char、boolean)中不存在无符号型。而在 c#中(bool、byte、char、decimal、double、float、int 32、long、sbyte、short、uint、ulong、ushort)有无符号型。所以在使用数据类型上需要注意对应。此外,二者在布尔类型上也有差异。从转换的 byte[]中可以看出来:

    //java
    true:[-1]
    false:[0]
    //c#
    true:01 # 幸运的是所有非零值转bool都是true。BitConverter.ToBoolean(BitConverter.GetBytes((sbyte)-1))
    false:00
    

    在 java 中,short 是 4 字节的,而 c#中仅为 2 字节:

    //java
    System.out.println("short:" + Arrays.toString(Bytes.toBytes('d')));
    short:[0, 0, 0, 100] # 十进制
    //c#
    Console.WriteLine($"short:{BitConverter.ToString(((short)'d').ToBytes())}");# 注意:方法ToBytes获取了bytes并反转了大小端
    short:00-64 # 十六进制
    

    customer's class 与 byte array 之间的转换

    为了提高工作效率,需要做一些转换工具,以此来减少 byte[]与用户数据之间的徒手转换工作。

    1. 为 hbase 仓储而创建的特性与接口

    特性类 HbaseTableAttribute 构造函数接受一个可选参数,以此指明与 hasee 对应的表名。

    public HbaseTableAttribute(string table = null)
    

    特性类 HbaseColumnAttribute 构造函数接受两个可选参数,以此指明列簇名与列名

    public HbaseColumnAttribute(string family = DefaultFamily, string column = null)
    

    接口 IHbaseTable 强制提供 byte[]转换为用户类时提供的 RowKey 属性。

    public interface IHbaseTable
    {
        string RowKey { get; set; }
    }
    

    举一student类为例:

    [HbaseTable("student")]
    public class Student : IHbaseTable
    {
        public string RowKey { get; set; }
        [HbaseColumn]
        public string Name { get; set; }
        [HbaseColumn]
        public int Age { get; set; }
        [HbaseColumn]
        public bool IsWork { get; set; }
        public DateTime JoinSchool { get; set; }
        [HbaseColumn]
        public List<string> Hobbies { get; set; }
    }
    

    2.为 c# hbase 客户端与用户类之间转换创建的辅助工具类

    目前实现的转换接口有:

    public interface IHbaseParser
    {
        //用户类转 hbase 客户端
        List<Mutation> ToMutations<T>(T obj) where T : class;
        //用户类转 hbase 客户端
        BatchMutation ToBatchMutation<T>(T obj) where T : class, IHbaseTable;
        //用户类集合转 hbase 客户端
        List<BatchMutation> ToBatchMutations<T>(IEnumerable<T> objs) where T : class, IHbaseTable;
        // hbase 客户端转用户类
        T ToReal<T>(TRowResult trr) where T : class, IHbaseTable, new();
        // hbase 客户端转用户类集合
        List<T> ToReals<T>(IEnumerable<TRowResult> trrs) where T : class, IHbaseTable, new();
    }
    

    帮助性接口:

     public interface IHbaseHelper
    {
        //获取特性HbaseTableAttribute中的表名
        string GetTableName<T>() where T : class, new();
        //获取特性HbaseColumnAttribute中的列簇名
        List<string> GetTableColumnNames<T>() where T : class, new();
    }
    

    使用方式如:

    //c# hbase 客户端准备
    var _clientTransport = new TSocketClientTransport(IPAddress.Loopback, 9090);
    var protocol = new TBinaryProtocol(_clientTransport);
    var _client = new Hbase.Client(protocol);
    var _hbaseHelper = new HbaseHelper();
    var _HbaseParser = new HbaseParser();
    
    var cancel = new CancellationToken();
    //在hbase创建表
    var table = _hbaseHelper.GetTableName<Student>();
    var colNames = _hbaseHelper.GetTableColumnNames<Student>();
    var columnFamilies = colNames
        .Select(t => new ColumnDescriptor { Name = t.ToBytes() })
        .ToList();
    await _client.createTableAsync(table.ToBytes(), columnFamilies, cancel);
    
    
    //写入1000条数据
    var range = Enumerable.Range(0, 1000).ToList();
    var students = range
        .Select(t => new Student { RowKey = t.ToString().Reverse2String(), Name = $"hsx{t}", Age = t })
        .ToList();
    students.Last().Hobbies = new List<string> { "running", "dance" };
    students.Last().IsWork = true;
    var batchs = _HbaseParser.ToBatchMutations(students);
    await _client.mutateRowsAsync(table.ToBytes(), batchs, null, cancel);
    
    
    //从‘0’开始扫描数据
    var id = await _client.scannerOpenAsync(
        table.ToBytes(),
        "0".ToBytes(),
        null,
        null,
        cancel);
    students = new List<Student>();
    List<Student> perStudents = null;
    do
    {
        var tRows = await _client.scannerGetListAsync(id, 500, cancel);
        perStudents = _HbaseParser.ToReals<Student>(tRows);
        students.AddRange(perStudents);
    } while (perStudents?.Count > 0);
    

    总结

    虽然我们可以进行 hbase 的读写,但是效率上的差距依然明显,基本在几倍的读性能差距上,写数据性能差异尚未测试。作为 net 了解 hbase 的一个学习窗口吧。

    更多细节,请查看HbaseNetCore项目。c# hbase 客户端基本告一段落,如有更新,也基本只会出现在本项目源代码中。

  • 相关阅读:
    为博客园选择一个小巧霸气的语法高亮插件
    再议 js 数字格式之正则表达式
    [扯蛋] 项目说
    浅谈 js 语句块与标签
    Yii 自定义模型路径
    js小记 function 的 length 属性
    js拾遗:appendChild 添加移动节点
    浅谈 IE下innerHTML导致的问题
    浅谈 js 数字格式类型
    [hihoCoder] 第四十九周: 欧拉路·一
  • 原文地址:https://www.cnblogs.com/hsxian/p/11326485.html
Copyright © 2011-2022 走看看