分布式数据访问服务之1—武夷山论剑篇

引言

     
业界主流的互连网架构中,分布式服务框架、分布式数据访问服务、音讯队列服务、服务网关(API)、分布式事务等都是骨干的零部件和框架。

  高校读的是一个很风尚的业内, 学了四年的玩耍比赛. 可惜没学好. 但认真过, 比做什么都认真. 见证了 
……

当大家的系统规模更为大,从几台服务器增添到几十台、几百台、上千台,传统的烟囱式的、大集中式系统架构,逐步演进为服务化SOA、分布式的种类架构:

打的所有游戏人物中 分享一位最疼爱 的

在数码层面,应用规模,访问层面和询问方面,全都以分布式的协会来搭建,使任何种类不设有质量和横向增加的瓶颈,达成系统的弹性伸缩和横向增添,

  澳门美高梅手机网站 1

以支撑大家超大规模的系统。

    
    “I’ve been alone for 10 thousand years. 我孤单了一万年。”

     
近日研商了业界主流的分布式数据访问服务,做了两回横向相比较和剖析。基于相比较做技术选型、验证,目的就是要设计一套.Net版本的分布式数据访问组件。

    

     
搞了这么多年.Net,ORMapping(EF、Hibernate、MyBatis),有须求搞一个“分布式数据访问服务”的技艺专题,作为计算和技术分享。

    “What I may be,whatever I may become in this world,know that I
will always look out of you,Tyrande.
无论自身做了何等,无论自身成为何样体统,我会永远关心你的,泰兰德。”

      这么些系列的首先篇以”黄山论剑”
开头吧,细数各门各派的看家本领(分布式数据访问服务框架),一较高低!

 

一、微软Azure SQL 

前言

动用 Azure SQL
数据库的可缩放工具和作用,可以轻松地横向增添数据库。更加是可以使用弹性数据库客户端库来创制和管理增添的数据库。

  上几次 写了两篇 关于 C json 引擎 解析和 构造内容

此意义支撑使用过三个 AzureSQL
数据库,轻松地付出数据分区应用程序。然后,可以应用弹性作业支持简化这么些数据库的管住。

  C json实战引擎 一 ,
完成解析部分

它包蕴了两个主导组件:分片映射管理、数据路由、多分片查询

  C json实战引擎 二 ,
达成结构部分

1. 分片映射管理器:分片映射管理器是一个特其余数据库,它爱惜一个分片集中有关所有分片
(数据库)的大局映射音讯。

    结合方面加上当前那篇, 一个高作用用的 cjson 引擎就出生了.

澳门美高梅手机网站 2

  

澳门美高梅手机网站 3

  今日, 分享那篇, 至少可以让我们, 学到简单深拷贝. 一些构造技巧. 还有就是cjson的帮扶函数使用!

2. 数量路由:使用查询中的数据将请求路由到相应数据库的功力

平等先分享一个函数 封装 初步.  

假使将一个伸手传入应用程序。基于请求的分区键值,应用程序必须依照该键值判断正确的数据库。接着,它会与数据库建立连接来拍卖请求。凭借数据着重路由

/*
* 这个代码是 对 strdup 的再实现, 调用之后需要free
* str        : 待复制的源码内容
*            : 返回 复制后的串内容
*/
char* 
str_dup(const char* str)
{
    int len; 
    char* nstr;
    DEBUG_CODE({
        if (NULL == str) {
            SL_WARNING("check is NULL == str!!");
            return NULL;
        }
    });

    len = sizeof(char) * (strlen(str) + 1);
    if (!(nstr = malloc(len))) {
        SL_FATAL("malloc is error! len = %d.", len);
        return NULL;
    }
    // 返回最后结果
    return memcpy(nstr, str, len);
}

可见透过对应用程序的分片映射的单个简单调用打开连接,举行数据操作:

老代码中 有过 strdup 深拷贝 字符串, 前边各种平台支撑的差距 _strdup
__strdup , strdup 各样. 前面为了统一写了一个.

澳门美高梅手机网站 4

其中 DEBUG_CODE 是对 assert 宏的扩展

3.  多分片查询:多分片查询用于诸如数码搜集/报告等急需跨多少个分片运行查询的职务。(相比较之下,数据相关的路由会在单个分片上推行所有操作。)

/*
 * 10.1 这里是一个 在 DEBUG 模式下的测试宏 
 *    
 * 用法 :
 * DEBUG_CODE({
 *        puts("debug start...");    
 * });
 */
#ifndef DEBUG_CODE
# ifdef _DEBUG
#    define DEBUG_CODE(code) code
# else
#    define DEBUG_CODE(code) 
# endif // ! _DEBUG
#endif // !DEBUG_CODE

当一个请求涉及三个(或享有)分片时,多分片查询将生效。多分片查询在具有分片或一组分片上推行同一的
T-SQL 代码。使用 UNION ALL 语义,


_DEBUG方式下开启 测试代码, 没有_DEBUG处于发表景况的时候,那段检测代码编译的时候会被去掉. 为了功效而生.

将涉足分片中的结果合并到一个计算果集中。该作用是通过该客户端库处理四个义务公开的,其中囊括连日来管理、线程管理、故障处理和中间结果处理。

对于SL_FATAL 是 sclog.h 接口中 一个 内部错误 日志宏. sclog.h 这几个日志记录类, 也很不错. 前边博文中

最多能够查询数百个分片

  C 基础框架开发

澳门美高梅手机网站 5

末段那有些 讲解了 sclog.h 代码, 当时讲的的太多了,应该分别细说的. sclog.h 思路照旧很不利的, 基本上业界大体也是其一思路.

小结:微软的Azure
SQL是一个PaaS层的数据访问服务,须要将本地数据库迁移到Azure
SQL,即云端SQLServer中,

多用户, 分级等手段. 前言已经介绍完了. 前边就轻易了, 都可以不看了.

整个Azure
SQL援救对SQLServer的横向增加,在SDK层面简化屏蔽了分库分表带来的有些编程难题,要是选用Azure
SQL的话,

 

分库分表的安排、管理、集成的确万分有利于,原生援救。不过本地化私有云环境下,SQLServer的分库分表就需求自己搞了。

正文

二、阿里分布式关系型数据库服务DRDS——云端数据库PaaS服务

1. cjson 辅助操作添加的接口

DRDS
近期固定成一个中间件(前身是TDDL),在事情应用和RDS(关系型数据库服务器)之间,本身不承担数据存储,

  新加的接口如下

只担负解决分布式意况下数据操作路由、执行、数据处理等功用。主要帮忙的数据库是MySQL。

// --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------

/*
 * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
 * b        : bool 值 最好是 _Bool
 *            : 返回 创建好的json 内容
 */
extern cjson_t cjson_newnull();
extern cjson_t cjson_newbool(int b);
extern cjson_t cjson_newnumber(double vd);
extern cjson_t cjson_newstring(const char* vs);
extern cjson_t cjson_newarray(void);
extern cjson_t cjson_newobject(void);

/*
 * 按照类型,创建 对映类型的数组 cjson对象
 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
 * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
 * type        : 类型目前支持 上面几种类型
 * array    : 数组原始数据
 * len        : 数组中元素长度
 *            : 返回创建的数组对象
 */
extern cjson_t cjson_newtypearray(int type, const void* array, int len);

/*
 * 将 jstr中 不需要解析的字符串都去掉
 * jstr        : 待处理的json串
 *            : 返回压缩后的json串内容
 */
extern char* cjson_mini(char* jstr);

/*
 *    将json文件解析成json内容返回. 需要自己调用 cjson_delete
 * jpath    : json串路径
 *            : 返回处理好的cjson_t 内容,失败返回NULL
 */
extern cjson_t cjson_dofile(char* jpath);

/*
 * 在array中分离第idx个索引项内容.
 * array    : 待处理的json_t 数组内容
 * idx        : 索引内容
 *            : 返回分离的json_t内容
 */
extern cjson_t cjson_detacharray(cjson_t array, int idx);

/*
 * 在object json 中分离 key 的项出去
 * object    : 待分离的对象主体内容
 * key        : 关联的键
 *            : 返回分离的 object中 key的项json_t
 */
extern cjson_t cjson_detachobject(cjson_t object, const char* key);

澳门美高梅手机网站 6澳门美高梅手机网站 7

前方是开创, 前面是分开, 分离的情致就是距离和上一轮没关系了. 线程也有分其余概念. 分离后自己自行回收.

DRDS的选拔与MySQL分外类似,建实例、建库、建表、SQL操作,唯一比较大的不一样在于水平拆分情势下,DRDS对于建表必要指定拆分字段(类似索引),

其中 cjson_mini 是为着去掉 cjson 字符串中 无用的字符内容.

只要带上这些拆分字段,SQL只会在有的数据分片上举行,从而加快SQL执行进度。

 

DRDS有着相比完好的MySQL SQL包容性

2. 摘录其中出色的接口,分析规划

DRDS
对于单机事务完整协理,也就是事情中一个工作中的各类SQL最终都达到同一个数据库即可保险强一致,

  首先看望下边一个函数

对于跨数据库的分布式事务,DRDS提供最终一致分布式事务给业务使用,近期高居内测阶段。

/*
 * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
 * jstr        : 待处理的json串
 *            : 返回压缩后的json串内容
 */
char* 
cjson_mini(char* jstr)
{
    char* in = jstr;
    char* to = jstr;
    char c;
    while(!!(c=*to)){
        if(sh_isspace(c)) ++to;
        else if(c == '/' && to[1] == '/') while((c=*++to) && c!='\n');
        else if(c == '/' && to[1] == '*'){
            while((c=*++to) && !(c=='*' && to[1] =='/'))
                ;
            if(c) to+=2;
        }
        else if(c=='"'){
            *in++ = c;
            while((c=*++to) && (c!='"' || to[-1]=='\\'))
                *in++ = c;
            if(c) {
                *in++=c;
                ++to;
            }
        }
        else
            *in++ = *to++;
    }

    *in = '\0';
    return jstr;
}

1. DRDS基本原理-读写分离:

思路是 先去掉空格字符, 前面去掉 // 和 /* */ 注释内容. 再对此
“” 包裹的字符串特殊处理. 此外字符稳如龙虎山 . 其中 sh_isspace 宏设计

对此sql举办项目判定,倘若判断为读取操作,则根据用户设置的读权重举行sql路由,到主实例依然到只读上展开sql操作。

如下

澳门美高梅手机网站 8

/*
 * c 如果是空白字符返回 true, 否则返回false
 * c : 必须是 int 值,最好是 char 范围
 */
#define sh_isspace(c) \
    ((c==' ')||(c>='\t'&&c<='\r'))

2. DRDS基本原理-水平拆分

思路也很巧妙. 都是做事总括. 不用谢.

对于sql进行项目判定,要是判断为读取操作,则根据用户安装的读权重进行sql路由,到主实例仍旧到只读上拓展sql操作。

再有一个 营造数组对象代码

DRDS中的数据是依据拆分字段值,加上一定的算法进行测算,按照结果存储数据到相应分片。

/*
 * 按照类型,创建 对映类型的数组 cjson对象
 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
 * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
 * type        : 类型目前支持 上面几种类型
 * array    : 数组原始数据
 * len        : 数组中元素长度
 *            : 返回创建的数组对象
 */
cjson_t 
cjson_newtypearray(int type, const void* array, int len)
{
    int i;
    cjson_t n = NULL, p = NULL, a;
    // _DEBUG 模式下简单检测一下
    DEBUG_CODE({
        if(type < _CJSON_FALSE || type > _CJSON_STRING || len <=0){
            SL_FATAL("check param is error! type = %d, len = %d.", type, len);
            return NULL;
        }
    });

    // 这里是实际执行代码
    a = cjson_newarray();
    for(i=0; i<len; ++i){
        switch(type){
        case _CJSON_NULL: n = cjson_newnull(); break;
        case _CJSON_FALSE: n = cjson_newbool(array? ((char*)array)[i] : 0); break;
        case _CJSON_TRUE: n = cjson_newbool(array? ((char*)array)[i] : 1); break;
        case _CJSON_NUMBER: n = cjson_newnumber(((double*)array)[i]); break;
        case _CJSON_STRING: n = cjson_newstring(((char**)array)[i]);break;
        }
        if(i){ //有你更好
            p->next = n;
            n->prev = p;
        }
        else
            a->child = n;
        p = n;
    }
    return a;
}

澳门美高梅手机网站 9澳门美高梅手机网站 10

其中

  1. DRDS基本原理-SQL路由
    cjson_t n = NULL, p = NULL, a;

当用户SQL到DRDS时,DRDS会解析整个SQL含义,然后根据拆分字段的值和执行政策将SQL路由到对应分区举行实施。

是为了消弭编译器警告, 没有定义就利用未开首化值. 对于其中成立代码都是 普通的 分类型搞. 很大白话.

澳门美高梅手机网站 11

对此 对象 分离代码如下

4. DRDS基本原理-数据统一:

/*
 * 在object json 中分离 key 的项出去
 * object    : 待分离的对象主体内容
 * key        : 关联的键
 *            : 返回分离的 object中 key的项json_t
 */
cjson_t 
cjson_detachobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if(!object || !object->child || !key || !*key){
            SL_WARNING("check param is object:%p, key:%s.", object, key);
            return NULL;
        }
    });

    for(c=object->child; c && str_icmp(c->key, key); c=c->next)
        ;
    if(!c) {
        SL_WARNING("check param key:%s => vlaue is empty.", key);
        return NULL;
    }
    if(c->prev)
        c->prev->next = c->next;
    if(c->next)
        c->next->prev = c->prev;
    if(c == object->child)
        object->child = c->next;
    c->prev = c->next = NULL;
    return c;
}

假定一个SQL对应多个分片数据进行,DRDS会将各类分片重临的数据根据原始SQL语义举行统一。

首假设寻觅, 查找到了 从卓越链表中上传那些目的,重新创设链接关系. 其中 str_icmp 在眼前分析过
, 很巧妙

澳门美高梅手机网站 12

/*
 * 这是个不区分大小写的比较函数
 * ls        : 左边比较字符串
 * rs        : 右边比较字符串
 *            : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
 */
int 
str_icmp(const char* ls, const char* rs)
{
    int l, r;
    if(!ls || !rs)
        return (int)ls - (int)rs;

    do {
        if((l=*ls++)>='a' && l<='z')
            l -= 'a' - 'A';
        if((r=*rs++)>='a' && r<='z')
            r -= 'a' - 'A';
    } while(l && l==r);

    return l-r;
}

5. DRDS支持的SQL语法

末尾分享 一个 常用函数
, 从json文件中获得cjson_t 对象. 再扯一点, 多写代码,后边熟习后您会发觉, 代码就是最好的注释.

澳门美高梅手机网站 13

/*
 *    将json文件解析成json内容返回, 需要自己调用 cjson_delete
 * jpath    : json串路径
 *            : 返回处理好的cjson_t 内容,失败返回NULL
 */
cjson_t 
cjson_dofile(char* jpath)
{
    cjson_t root;
    tstring tstr = file_malloc_readend(jpath);
    if(!tstr){
        SL_WARNING("readend jpath:%s is error!", jpath);
        return NULL;
    }
    root = cjson_parse(tstr->str);
    tstring_destroy(&tstr);
    return root;
}

6. DRDS不支持的SQL语法:

地点关于 tstring 设计 具体看下边博文.

    受限于分布式事务

  C 封装一个通用链表 和
一个简单字符串开发库

  • 跨分片操作, UPDATE/DELETE [ORDER BY] LIMIT
  • 跨分片操作, UPDATE A,B set A.s = B.s+1 WHERE A.ID = B.NAME ,
    非拆分字段之间的跨库JOIN
  • 拆分键变更, UPDATE A SET A.ID = 1 WHERE XXX, ID为拆分字段
  • 跨分片操作, INSERT A SELECT B WHERE B.ID > XX , 跨库导入导出数据
  • 跨库事务, 比如两遍UPDATE不在一个分片上

到那边 关于cjson库 补充的 支持函数紧要规划就寿终正寝了.

   子查询范围

 

  • 暂不协助非where条件的correlate subquery
  • 暂不协助sql中带聚合条件和correlate subquery

3. cjson 定稿代码 总显得

  1. DRDS是或不是援助分布式join以及错综复杂SQL怎样帮衬?

cjson.h

 
 不是兼具的join都可以支持,例如对于大表之间的join,就因为实施代价过高,速度过慢而被DRDS限制,但大部分的join的语义,协助以最急迅的艺术形成。

澳门美高梅手机网站 14澳门美高梅手机网站 15

   DRDS处理join的原则万分简单:尽可能让join暴发在单机

#ifndef _H_CJSON
#define _H_CJSON

// json 中几种数据类型定义 , 对于C而言 最难的是看不见源码,而不是api复杂, 更不是业务复杂
#define _CJSON_FALSE    (0)
#define _CJSON_TRUE        (1)
#define _CJSON_NULL        (2)
#define _CJSON_NUMBER    (3)
#define _CJSON_STRING    (4)
#define _CJSON_ARRAY    (5)
#define _CJSON_OBJECT    (6)

#define _CJSON_ISREF    (256)        //set 时候用如果是引用就不释放了
#define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了

struct cjson {
    struct cjson *next, *prev;
    struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

    int type;
    char *key;    // json内容那块的 key名称     
    char *vs;    // type == _CJSON_STRING, 是一个字符串     
    double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

/*
 * 这个宏,协助我们得到 int 值 或 bool 值 
 * 
 * item : 待处理的目标cjson_t结点
 */
#define cjson_getint(item) \
    ((int)((item)->vd))

/*
 *  删除json串内容  
 * c        : 待释放json_t串内容
 */
extern void cjson_delete(cjson_t* pc);

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_parse(const char* jstr);

/*
 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 *推荐是数组使用
 * array    : 待处理的cjson_t数组对象
 *            : 返回这个数组中长度
 */
extern int cjson_getlen(cjson_t array);

/*
 * 根据索引得到这个数组中对象
 * array    : 数组对象
 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 *            : 返回查找到的当前对象
 */
extern cjson_t cjson_getarray(cjson_t array, int idx);

/*
 * 根据key得到这个对象 相应位置的值
 * object    : 待处理对象中值
 * key        : 寻找的key
 *            : 返回 查找 cjson_t 对象
 */
extern cjson_t cjson_getobject(cjson_t object, const char* key);


// --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------

/*
 *  这里是将 cjson_t item 转换成字符串内容,需要自己free 
 * item        : cjson的具体结点
 *            : 返回生成的item的json串内容
 */
extern char* cjson_print(cjson_t item);

// --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------

/*
 * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
 * b        : bool 值 最好是 _Bool
 *            : 返回 创建好的json 内容
 */
extern cjson_t cjson_newnull();
extern cjson_t cjson_newbool(int b);
extern cjson_t cjson_newnumber(double vd);
extern cjson_t cjson_newstring(const char* vs);
extern cjson_t cjson_newarray(void);
extern cjson_t cjson_newobject(void);

/*
 * 按照类型,创建 对映类型的数组 cjson对象
 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
 * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
 * type        : 类型目前支持 上面几种类型
 * array    : 数组原始数据
 * len        : 数组中元素长度
 *            : 返回创建的数组对象
 */
extern cjson_t cjson_newtypearray(int type, const void* array, int len);

/*
 * 将 jstr中 不需要解析的字符串都去掉
 * jstr        : 待处理的json串
 *            : 返回压缩后的json串内容
 */
extern char* cjson_mini(char* jstr);

/*
 *    将json文件解析成json内容返回. 需要自己调用 cjson_delete
 * jpath    : json串路径
 *            : 返回处理好的cjson_t 内容,失败返回NULL
 */
extern cjson_t cjson_dofile(char* jpath);

/*
 * 在array中分离第idx个索引项内容.
 * array    : 待处理的json_t 数组内容
 * idx        : 索引内容
 *            : 返回分离的json_t内容
 */
extern cjson_t cjson_detacharray(cjson_t array, int idx);

/*
 * 在object json 中分离 key 的项出去
 * object    : 待分离的对象主体内容
 * key        : 关联的键
 *            : 返回分离的 object中 key的项json_t
 */
extern cjson_t cjson_detachobject(cjson_t object, const char* key);

#endif // !_H_CJSON

   7.1.
依照同一个维度举办切分:若是可以让join物理上爆发在单台机器上,那么别的一类的繁杂查询都是可以直接帮忙的。一般而言,

View Code

那就表示加入join的多张表根据同一个维度举行切分。例如,一个用户有四个商品,每个商品都有投机的货品特性。那时候,倘若需求join,可以将有着数据根据用户,

 

照旧依照商品举办切分,那么join就会大体上的发出在同一个机器上,DRDS可以很自在的担保拥有在单机暴发的join查询,物理上都可以意识到数据,

cjson.c

对此格外复杂的SQL,可以通过注释的法子,直接报告DRDS切分条件,那样就可以绕开SQL解析器举行询问。

澳门美高梅手机网站 16澳门美高梅手机网站 17

  7.2.
小表复制(主数据复制同步):同时可以有取舍的将有些不日常更新的,数据量比较小的元数据表复制到全体的节点上。

#include <cjson.h>
#include <schead.h>
#include <sclog.h>
#include <tstring.h>
#include <float.h>
#include <math.h>

// 删除cjson
static void __cjson_delete(cjson_t c)
{
    cjson_t next;
    while (c) {
        next = c->next;
        //递归删除儿子
        if (!(c->type & _CJSON_ISREF)) {
            if (c->child) //如果不是尾递归,那就先递归
                __cjson_delete(c->child);
            if (c->vs)
                free(c->vs);
        }
        else if (!(c->type & _CJSON_ISCONST) && c->key)
            free(c->key);
        free(c);
        c = next;
    }
}

/*
*  删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c        : 待释放json_t串内容
*/
void 
cjson_delete(cjson_t* pc)
{
    if (!pc || !*pc)
        return;
    __cjson_delete(*pc);
    *pc = NULL;
}

//构造一个空 cjson 对象
static inline cjson_t __cjson_new(void)
{
    cjson_t c = calloc(1, sizeof(struct cjson));
    if (!c) {
        SL_FATAL("calloc sizeof struct cjson error!");
        exit(_RT_EM);
    }
    return c;
}

// 简化的代码段,用宏来简化代码书写 , 16进制处理
#define __parse_hex4_code(c, h) \
    if (c >= '0' && c <= '9') \
        h += c - '0'; \
    else if (c >= 'A' && c <= 'F') \
        h += 10 + c - 'A'; \
    else if (c >= 'a' && c <= 'z') \
        h += 10 + c - 'F'; \
    else \
        return 0

// 等到unicode char代码
static unsigned __parse_hex4(const char* str)
{
    unsigned h = 0;
    char c = *str;
    //第一轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第二轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第三轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第四轮
    __parse_hex4_code(c, h);

    return h;
}

// 分析字符串的子函数,
static const char* __parse_string(cjson_t item, const char* str)
{
    static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
    const char *ptr;
    char *nptr, *out;
    int len;
    char c;
    unsigned uc, nuc;

    if (*str != '\"') { // 检查是否是字符串内容
        SL_WARNING("need \\\" str => %s error!", str);
        return NULL;
    }

    for (ptr = str + 1, len = 0; (c = *ptr++) != '\"' && c; ++len)
        if (c == '\\') //跳过转义字符
            ++ptr;
    if (!(out = malloc(len + 1))) {
        SL_FATAL("malloc %d size error!", len + 1);
        return NULL;
    }
    // 这里复制拷贝内容
    for (ptr = str + 1, nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
        if (c != '\\') {
            *nptr++ = c;
            continue;
        }
        // 处理转义字符
        switch ((c = *++ptr)) {
        case 'b': *nptr++ = '\b'; break;
        case 'f': *nptr++ = '\f'; break;
        case 'n': *nptr++ = '\n'; break;
        case 'r': *nptr++ = '\r'; break;
        case 't': *nptr++ = '\t'; break;
        case 'u': // 将utf16 => utf8, 专门的utf处理代码
            uc = __parse_hex4(ptr + 1);
            ptr += 4;//跳过后面四个字符, unicode
            if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)    break;    /* check for invalid.    */

            if (uc >= 0xD800 && uc <= 0xDBFF)    /* UTF16 surrogate pairs.    */
            {
                if (ptr[1] != '\\' || ptr[2] != 'u')    
                    break;    /* missing second-half of surrogate.    */
                nuc = __parse_hex4(ptr + 3);
                ptr += 6;
                if (nuc < 0xDC00 || nuc>0xDFFF)        
                    break;    /* invalid second-half of surrogate.    */
                uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF));
            }

            len = 4;
            if (uc < 0x80) 
                len = 1;
            else if (uc < 0x800) 
                len = 2;
            else if (uc < 0x10000) 
                len = 3; 
            nptr += len;

            switch (len) {
            case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 1: *--nptr = (uc | __marks[len]);
            }
            nptr += len;
            break;
        default: *nptr++ = c;
        }
    }

    *nptr = '\0';
    if (c == '\"')
        ++ptr;
    item->vs = out;
    item->type = _CJSON_STRING;
    return ptr;
}

// 分析数值的子函数,写的可以
static const char* __parse_number(cjson_t item, const char* str)
{
    double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
    int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
    char c;

    if ((c = *str) == '-' || c == '+') {
        ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
        ++str;
    }
    //处理整数部分
    for (c = *str; c >= '0' && c <= '9'; c = *++str)
        n = n * 10 + c - '0';
    if (c == '.')
        for (; (c = *++str) >= '0' && c <= '9'; --nd)
            n = n * 10 + c - '0';

    // 处理科学计数法
    if (c == 'e' || c == 'E') {
        if ((c = *++str) == '+') //处理指数部分
            ++str;
        else if (c == '-')
            es = -1, ++str;
        for (; (c = *str) >= '0' && c <= '9'; ++str)
            e = e * 10 + c - '0';
    }

    //返回最终结果 number = +/- number.fraction * 10^+/- exponent
    n = ns * n * pow(10.0, nd + es * e);
    item->vd = n;
    item->type = _CJSON_NUMBER;
    return str;
}

// 跳过不需要处理的字符
static const char* __skip(const char* in)
{
    if (in && *in && *in <= 32) {
        unsigned char c;
        while ((c = *++in) && c <= 32)
            ;
    }
    return in;
}

// 递归下降分析 需要声明这些函数
static const char* __parse_array(cjson_t item, const char* str);
static const char* __parse_object(cjson_t item, const char* str);
static const char* __parse_value(cjson_t item, const char* value);

// 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '[') {
        SL_WARNING("array str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_ARRAY;
    str = __skip(str + 1);
    if (*str == ']') // 低估提前结束
        return str + 1;

    item->child = child = __cjson_new();
    str = __skip(__parse_value(child, str));
    if (!str) {//解析失败 直接返回
        SL_WARNING("array str error e n d one: %s.", str);
        return NULL;
    }
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_value(child, __skip(str + 1)));
        if (!str) {// 写代码是一件很爽的事
            SL_WARNING("array str error e n d two: %s.", str);
            return NULL;
        }
    }

    if (*str != ']') {
        SL_WARNING("array str error e n d: %s.", str);
        return NULL;
    }
    return str + 1; // 跳过']'
}

// 分析对象的子函数
static const char* __parse_object(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '{') {
        SL_WARNING("object str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_OBJECT;
    str = __skip(str + 1);
    if (*str == '}')
        return str + 1;

    //处理结点, 开始读取一个 key
    item->child = child = __cjson_new();
    str = __skip(__parse_string(child, str));
    if (!str || *str != ':') {
        SL_WARNING("__skip __parse_string is error : %s!", str);
        return NULL;
    }
    child->key = child->vs;
    child->vs = NULL;

    str = __skip(__parse_value(child, __skip(str + 1)));
    if (!str) {
        SL_WARNING("__skip __parse_string is error 2!");
        return NULL;
    }

    // 递归解析
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_string(child, __skip(str + 1)));
        if (!str || *str != ':'){
            SL_WARNING("__parse_string need name or no equal ':' %s.", str);
            return NULL;
        }
        child->key = child->vs;
        child->vs = NULL;

        str = __skip(__parse_value(child, __skip(str+1)));
        if (!str) {
            SL_WARNING("__parse_string need item two ':' %s.", str);
            return NULL;
        }
    }

    if (*str != '}') {
        SL_WARNING("object str error e n d: %s.", str);
        return NULL;
    }
    return str + 1;
}

// 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
    char c; 
    if ((value) && (c = *value)) {
        switch (c) {
            // n = null, f = false, t = true
        case 'n' : return item->type = _CJSON_NULL, value + 4;
        case 'f' : return item->type = _CJSON_FALSE, value + 5;
        case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
        case '\"': return __parse_string(item, value);
        case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
        case '+' : case '-': return __parse_number(item, value);
        case '[' : return __parse_array(item, value);
        case '{' : return __parse_object(item, value);
        }
    }
    // 循环到这里是意外 数据
    SL_WARNING("params value = %s!", value);
    return NULL;
}

/*
* 对json字符串解析返回解析后的结果
* jstr        : 待解析的字符串
*            : 返回解析好的字符串内容
*/
cjson_t 
cjson_parse(const char* jstr)
{
    cjson_t c = __cjson_new();
    const char* end;

    if (!(end = __parse_value(c, __skip(jstr)))) {
        SL_WARNING("__parse_value params end = %s!", end);
        cjson_delete(&c);
        return NULL;
    }

    //这里是否检测 返回测试数据
    return c;
}

/*
* 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
*推荐是数组使用
* array    : 待处理的cjson_t数组对象
*            : 返回这个数组中长度
*/
int 
cjson_getlen(cjson_t array)
{
    int len = 0;
    if (array)
        for (array = array->child; array; array = array->next)
            ++len;

    return len;
}

/*
* 根据索引得到这个数组中对象
* array    : 数组对象
* idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
*            : 返回查找到的当前对象
*/
cjson_t 
cjson_getarray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if (!array || idx < 0) {
            SL_FATAL("array:%p, idx=%d params is error!", array, idx);
            return NULL;
        }
    });

    for (c = array->child; c&&idx > 0; c = c->next)
        --idx;

    return c;
}

/*
* 根据key得到这个对象 相应位置的值
* object    : 待处理对象中值
* key        : 寻找的key
*            : 返回 查找 cjson_t 对象
*/
cjson_t 
cjson_getobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if (!object || !key || !*key) {
            SL_FATAL("object:%p, key=%s params is error!", object, key);
            return NULL;
        }
    });

    for (c = object->child; c && str_icmp(key, c->key); c = c->next)
        ;

    return c;
}

// --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------

// 2^n>=x , n是最小的整数
static int __pow2gt(int x)
{
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return x + 1;
}

/*
 *     这里使用 tstring 结构 size 这里表示 字符串总大小,没有变化
 * len 表示当前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的
 */
static char* __ensure(tstring p, int need)
{
    char* nbuf;
    int nsize;
    if (!p || !p->str) {
        SL_FATAL("p:%p need:%p is error!", p, need);
        return NULL;
    }
    need += p->len;
    if (need <= p->size) //内存够用直接返回结果
        return p->str + p->len;
    nsize = __pow2gt(need);
    if ((nbuf = malloc(nsize*sizeof(char))) == NULL) {
        free(p->str);
        p->size = p->len = 0;
        p->str = NULL;
        SL_FATAL("malloc nsize = %d error!", nsize);
        return NULL;
    }
    //这里复制内容
    memcpy(nbuf, p->str, p->size);
    free(p->str);
    p->size = nsize;
    p->str = nbuf;
    return nbuf + p->len;
}

// 这里更新一下 当前字符串, 返回当前字符串的长度
inline static int __update(tstring p) 
{
    return (!p || !p->str) ? 0 : p->len + strlen(p->str+p->len);
}

// 将item 中值转换成字符串 保存到p中
static char* __print_number(cjson_t item, tstring p)
{
    char* str = NULL;
    double d = item->vd;
    int i = (int)d;

    if (d == 0) {  //普通0
        str = __ensure(p, 2);
        if (str)
            str[0] = '0', str[1] = '\0';
    }
    else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
        str = __ensure(p, 21); //int 值 
        if (str)
            sprintf(str, "%d", i);
    }
    else {
        str = __ensure(p, 64); //double值 
        if (str) {
            double nd = fabs(d); //得到正值开始比较
            if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
                sprintf(str, "%.0f", d);
            else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
                sprintf(str, "%e", d);
            else
                sprintf(str, "%f", d);

        }
    }

    return str;
}

// 输出字符串内容
static char* __print_string(char* str, tstring p)
{
    const char* ptr;
    char *nptr, *out;
    int len = 0, flag = 0;
    unsigned char c;

    if (!str || !*str) { //最特殊情况,什么都没有 返回NULL
        out = __ensure(p, 3);
        if (!out)
            return NULL;
        out[0] = '\"', out[1] = '\"', out[2] = '\0';
        return out;
    }


    for (ptr = str; (c=*ptr); ++ptr)
        flag |= ((c > 0 && c < 32) || c == '\"' || c == '\\');

    if (!flag) {  //没有特殊字符直接处理结果
        len = ptr - str;
        out = __ensure(p,len + 3);
        if (!out)
            return NULL;
        nptr = out;
        *nptr++ = '\"';
        strcpy(nptr, str);
        nptr[len] = '\"';
        nptr[len + 1] = '\0';
        return out;
    }

    //处理 存在 "和转义字符内容
    for (ptr = str; (c = *ptr) && ++len; ++ptr) {
        if (strchr("\"\\\b\f\n\r\t", c))
            ++len;
        else if (c < 32) //隐藏字符的处理, 这里可以改
            len += 5;
    }

    if ((out = __ensure(p, len + 3)) == NULL)
        return NULL;
    //先添加 \"
    nptr = out;
    *nptr++ = '\"';
    for (ptr = str; (c = *ptr); ++ptr) {
        if (c > 31 && c != '\"' && c != '\\') {
            *nptr++ = c;
            continue;
        }
        *nptr++ = '\\';
        switch (c){
        case '\\':    *nptr++ = '\\';    break;
        case '\"':    *nptr++ = '\"';    break;
        case '\b':    *nptr++ = 'b';    break;
        case '\f':    *nptr++ = 'f';    break;
        case '\n':    *nptr++ = 'n';    break;
        case '\r':    *nptr++ = 'r';    break;
        case '\t':    *nptr++ = 't';    break;
        default: sprintf(nptr, "u%04x", c);nptr += 5;    /* 不可见字符 采用 4字节字符编码 */
        }
    }
    *nptr++ = '\"';
    *nptr = '\0';
    return out;
}

//这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
static char* __print_value(cjson_t item, tstring p);
static char* __print_array(cjson_t item, tstring p);
static char* __print_object(cjson_t item, tstring p);

// 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
static char* __print_value(cjson_t item, tstring p) 
{
    char* out = NULL;
    switch ((item->type) & UCHAR_MAX) { // 0xff
    case _CJSON_FALSE: if ((out = __ensure(p, 6))) strcpy(out, "false"); break;
    case _CJSON_TRUE: if ((out = __ensure(p, 5))) strcpy(out, "true"); break;
    case _CJSON_NULL: if ((out = __ensure(p, 5))) strcpy(out, "null"); break;
    case _CJSON_NUMBER:    out = __print_number(item, p); break;
    case _CJSON_STRING:    out = __print_string(item->vs, p); break;
    case _CJSON_ARRAY:    out = __print_array(item, p); break;
    case _CJSON_OBJECT:    out = __print_object(item, p); break;
    }

    return out;
}

// 同样 假定 item 和 p都是存在且不为NULL
static char* __print_array(cjson_t item, tstring p)
{
    char* ptr;
    cjson_t child = item->child;
    int ncut, i;
    // 得到孩子结点的深度
    for (ncut = 0; (child); child = child->child)
        ++ncut;
    if (!ncut) { //没有孩子结点 直接空数组返回结果
        char* out = NULL;
        if (!(out = __ensure(p, 3))) 
            strcpy(out, "[]");
        return out;
    }

    i = p->len;
    if (!(ptr = __ensure(p, 1)))
        return NULL;
    *ptr = '[';
    ++p->len;
    for (child = item->child; (child); child = child->next) {
        __print_value(child, p);
        p->len = __update(p);
        if (child->next) {
            if (!(ptr = __ensure(p, 2)))
                return NULL;
            *ptr++ = ',';
            *ptr = '\0';
            p->len += 1;
        }
    }
    if (!(ptr = __ensure(p, 2)))
        return NULL;
    *ptr++ = ']';
    *ptr = '\0';
    return p->str + i;

}

// 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
static char* __print_object(cjson_t item, tstring p)
{
    char* ptr;
    int i, ncut, len;
    cjson_t child = item->child;

    // 得到孩子结点的深度
    for (ncut = 0; child; child = child->child)
        ++ncut;
    if (!ncut) {
        char* out = NULL;
        if (!(out = __ensure(p, 3)))
            strcpy(out, "{}");
        return out;
    }

    i = p->len;
    if (!(ptr = __ensure(p, 2)))
        return NULL;
    *ptr++ = '{';
    *ptr -= '\0';
    p->len += 1;
    // 根据子结点 处理
    for (child = item->child; (child); child = child->next) {
        __print_string(child->key, p);
        p->len = __update(p);

        //加入一个冒号
        if (!(ptr = __ensure(p, 1)))
            return NULL;
        *ptr++ = ':';
        p->len += 1;

        //继续打印一个值
        __print_value(child, p);
        p->len = __update(p);

        //结算最后内容
        len = child->next ? 1 : 0;
        if ((ptr = __ensure(p, len + 1)) == NULL)
            return NULL;
        if (child->next)
            *ptr++ = ',';
        *ptr = '\0';
        p->len += len;
    }
    if (!(ptr = __ensure(p, 2)))
        return NULL;
    *ptr++ = '}';
    *ptr = '\0';
    return p->str + i;
}

#define _INT_CJONSTR    (256)
/*
*  这里是将 cjson_t item 转换成字符串内容,需要自己free
* item        : cjson的具体结点
*            : 返回生成的item的json串内容
*/
char* 
cjson_print(cjson_t item)
{
    struct tstring p;
    char* out;
    if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) {
        SL_FATAL("item:%p, p.str = malloc is error!", item);
        return NULL;
    }
    p.size = _INT_CJONSTR;
    p.len = 0;

    out = __print_value(item, &p); //从值处理开始, 返回最终结果
    if (out == NULL) {
        free(p.str);
        SL_FATAL("__print_value item:%p, p:%p is error!", item, &p);
        return NULL;
    }
    return realloc(out,strlen(out) + 1); // 体积变小 realloc返回一定成功
}

// --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------

/*
 * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
 * b        : bool 值 最好是 _Bool
 *            : 返回 创建好的json 内容
 */
cjson_t 
cjson_newnull()
{
    cjson_t item = __cjson_new();
    item->type = _CJSON_NULL; 
    return item;
}

cjson_t 
cjson_newbool(int b)
{
    cjson_t item = __cjson_new();
    item->vd = item->type = b ? _CJSON_TRUE : _CJSON_FALSE; 
    return item;
}

cjson_t 
cjson_newnumber(double vd)
{
    cjson_t item = __cjson_new();
    item->type = _CJSON_NUMBER;
    item->vd = vd;
    return item;
}

cjson_t 
cjson_newstring(const char* vs)
{
    cjson_t item = __cjson_new();
    item->type = _CJSON_STRING;
    item->vs = str_dup(vs);
    return item;
}

cjson_t 
cjson_newarray(void)
{
    cjson_t item = __cjson_new();
    item->type = _CJSON_ARRAY;
    return item;
}

cjson_t 
cjson_newobject(void)
{
    cjson_t item = __cjson_new();
    item->type = _CJSON_OBJECT;
    return item;
}

/*
 * 按照类型,创建 对映类型的数组 cjson对象
 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
 * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
 * type        : 类型目前支持 上面几种类型
 * array    : 数组原始数据
 * len        : 数组中元素长度
 *            : 返回创建的数组对象
 */
cjson_t 
cjson_newtypearray(int type, const void* array, int len)
{
    int i;
    cjson_t n = NULL, p = NULL, a;
    // _DEBUG 模式下简单检测一下
    DEBUG_CODE({
        if(type < _CJSON_FALSE || type > _CJSON_STRING || len <=0){
            SL_FATAL("check param is error! type = %d, len = %d.", type, len);
            return NULL;
        }
    });

    // 这里是实际执行代码
    a = cjson_newarray();
    for(i=0; i<len; ++i){
        switch(type){
        case _CJSON_NULL: n = cjson_newnull(); break;
        case _CJSON_FALSE: n = cjson_newbool(array? ((char*)array)[i] : 0); break;
        case _CJSON_TRUE: n = cjson_newbool(array? ((char*)array)[i] : 1); break;
        case _CJSON_NUMBER: n = cjson_newnumber(((double*)array)[i]); break;
        case _CJSON_STRING: n = cjson_newstring(((char**)array)[i]);break;
        }
        if(i){ //有你更好
            p->next = n;
            n->prev = p;
        }
        else
            a->child = n;
        p = n;
    }
    return a;
}

/*
 * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
 * jstr        : 待处理的json串
 *            : 返回压缩后的json串内容
 */
char* 
cjson_mini(char* jstr)
{
    char* in = jstr;
    char* to = jstr;
    char c;
    while(!!(c=*to)){
        if(sh_isspace(c)) ++to;
        else if(c == '/' && to[1] == '/') while((c=*++to) && c!='\n');
        else if(c == '/' && to[1] == '*'){
            while((c=*++to) && !(c=='*' && to[1] =='/'))
                ;
            if(c) to+=2;
        }
        else if(c=='"'){
            *in++ = c;
            while((c=*++to) && (c!='"' || to[-1]=='\\'))
                *in++ = c;
            if(c) {
                *in++=c;
                ++to;
            }
        }
        else
            *in++ = *to++;
    }

    *in = '\0';
    return jstr;
}

/*
 *    将json文件解析成json内容返回, 需要自己调用 cjson_delete
 * jpath    : json串路径
 *            : 返回处理好的cjson_t 内容,失败返回NULL
 */
cjson_t 
cjson_dofile(char* jpath)
{
    cjson_t root;
    tstring tstr = file_malloc_readend(jpath);
    if(!tstr){
        SL_WARNING("readend jpath:%s is error!", jpath);
        return NULL;
    }
    root = cjson_parse(tstr->str);
    tstring_destroy(&tstr);
    return root;
}

/*
 * 在array中分离第idx个索引项内容.
 * array    : 待处理的json_t 数组内容
 * idx        : 索引内容
 *            : 返回分离的json_t内容
 */
cjson_t 
cjson_detacharray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if(!array || idx<0){
            SL_WARNING("check param is array:%p, idx:%d.", array, idx);
            return NULL;
        }
    });

    for(c=array->child; idx>0 && c; c = c->next)
        --idx;
    if(c>0){
        SL_WARNING("check param is too dig idx:sub %d.", idx);
        return NULL;
    }
    //这里开始拼接了
    if(c->prev)
        c->prev->next = c->next;
    if(c->next)
        c->next->prev = c->prev;
    if(c == array->child)
        array->child = c->next;
    c->prev = c->next = NULL;
    return c;
}

/*
 * 在object json 中分离 key 的项出去
 * object    : 待分离的对象主体内容
 * key        : 关联的键
 *            : 返回分离的 object中 key的项json_t
 */
cjson_t 
cjson_detachobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if(!object || !object->child || !key || !*key){
            SL_WARNING("check param is object:%p, key:%s.", object, key);
            return NULL;
        }
    });

    for(c=object->child; c && str_icmp(c->key, key); c=c->next)
        ;
    if(!c) {
        SL_WARNING("check param key:%s => vlaue is empty.", key);
        return NULL;
    }
    if(c->prev)
        c->prev->next = c->next;
    if(c->next)
        c->next->prev = c->prev;
    if(c == object->child)
        object->child = c->next;
    c->prev = c->next = NULL;
    return c;
}

如此,大表join小表的时候,就从一个分布式join变为了地面join,当然,那种进程须求肯定的代价,也就是元数据表内的数额更新,

View Code

可能在一小段日子(50~100ms)后才能在分库内观看。

 

  7.3.
在线询问与离线查询分离:对于复杂的大表和大表的分析和总括类查询,阿里之中推荐应用专门的辨析引擎来取得报表数量,比如采取ODPS等,

总的而言 下边代码 加上注释七个公文一共
1100行左右,去掉注释应该在800行左右. 对于cjson库应该算很小的了. 很符合

那类查询利用传统数据库架构,在数据量相当了不起的时候,很可能会影响线上的应用,因而阿里指出将在线和离线查询分开。

读书,扩张或再优化.

总括:阿里的DRDS依然那一个强大的,借助于MySQL原生的Replica技术+SQL路由解析+内存数据统一,万般的MySQL分库分表、读写分离等大部分风貌都能支撑。

 

三、当当Sharding-JDBC

4. 测试代码展现

Sharding-JDBC是当当应用框架ddframe中,关系型数据库模块dd-rdb中分离出来的数据库水平扩大框架,即透明化数据库分库分表访问。

  对于地点代码又写了一个测试 , 首先 测试的 json文件内容

从框架名字看,肯定是Java技术路线的,官方的验证中,Sharding-JDBC功效灵活且周详:

firefighting_rule.json 内容如下

  • 分片策略灵活,可匡助=,BETWEEN,IN等多维度分片,
  • 也可协理多分片键共用
  • SQL解析功效完善,协助聚合,分组,排序,Limit,OR等查询,并且襄助Binding
    Table以及笛Carl积的表查询。
  • 帮助柔性事务(近来仅最大大力送达型)。
  • 支撑读写分离
{     
"firefighting_rule":     
 {    
  "key1":   
   {  
    "id":1,
    "dungeon_id":40008,
    "level_contain":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
    "active_time":[[1,"110000"],[4,"110000"],[5,"210000"]],
    "boss_ui_head":"UI_icon/IMG_ShiJieBoss_TouXiang.png",
    "activity_tag_icon":"IMG_GaiBan_HuoDong_ShiJieBoss_TuBiao.png",
    "activity_tag_word":"IMG_GaiBan_ZhuCheng_ShiJieBoss_TuBiao_MingZi.png",
    "activity_pic_json":"UI_HuoDong_ShiJieBoss.json",
    "jinbi_buff_icon":"UI_icon/IMG_WorldBoss_JinbiBuff_Atk.png",
    "jinbi_buff_damage":[[8,1000],[9,1000],[11,1000],[12,1000]],
    "jinbi_buff_price":10,
    "jinbi_buff_limit":999,
    "free_change":1,
    "refresh_price":20,
    "change_price":20,
    "show_hero_num":20 
   }  
 }    
}

澳门美高梅手机网站 18

 处理的剖析文件代码如下

Sharding-JDBC定位于一般数据库CRUD操作,如今仅针对DQL和DML语句进行帮助

#include <schead.h>
#include <sclog.h>
#include <cjson.h>

#define _STR_FILE "firefighting_rule.json"


/**
 * 这里 是解析 上面的json文件内容
 */
int main(int argc, char* argv[])
{
    INIT_PAUSE();
    // 开启日志记录功能
    sl_start();

    cjson_t rule = cjson_dofile(_STR_FILE);
    if (NULL == rule)
        CERR_EXIT("cjson_dofile " _STR_FILE " is error!");

    // 数据合法 这里开始得到一部分
    cjson_t firefighting_rule = cjson_detachobject(rule, "firefighting_rule");
    // 得到真正的内容
    cjson_t key1 = cjson_detachobject(firefighting_rule, "key1");

    //这里得到 key1 输出内容
    char* nkey = cjson_print(key1);
    if (NULL == nkey)
        CERR_EXIT("cjson_print key1 is error!");

    // 这里打印数据测试 
    puts(nkey);
    free(nkey);

    // 再简单测试一下 
    cjson_t id = cjson_getobject(key1, "id");
    printf("\nid = %d\n", cjson_getint(id));

    //得到数组对象 测试
    cjson_t level_contain = cjson_getobject(key1, "level_contain");
    printf("\ncount(level_contain) = %d\n", cjson_getlen(level_contain));

    cjson_delete(&key1);
    cjson_delete(&firefighting_rule);
    // rule 释放
    cjson_delete(&rule);

    return 0;
}

编程示例:

里头文件结构是 测试文件在 根目录下 或者和 exe或 out同超级目录下. 最后测试结果如下

1 ShardingRule shardingRule = ShardingRule.builder()
    .dataSourceRule(dataSourceRule)
    .tableRules(tableRuleList)
    .databaseShardingStrategy(new DatabaseShardingStrategy("sharding_column", new XXXShardingAlgorithm())) 
    .tableShardingStrategy(new TableShardingStrategy("sharding_column", new XXXShardingAlgorithm()))) 
    .build(); 
2 DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule); 
  String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?"; 
4     Connection conn = dataSource.getConnection(); 
5     PreparedStatement preparedStatement = conn.prepareStatement(sql)) {           
      preparedStatement.setInt(1, 10); 
6     preparedStatement.setInt(2, 1001); 
      ResultSet rs = preparedStatement.executeQuery();
8       {
                while(rs.next()) {
                System.out.println(rs.getInt(1));  
                System.out.println(rs.getInt(2));
        } 
      } 
9     

澳门美高梅手机网站 19

澳门美高梅手机网站 20

一切正常, 欢迎将这些cjson 库用在大团结 纯C的小品种中. 自己想怎么改就怎么改. 楼主不介意, 仍能帮您订制改. 

对于复制的归咎查询,咨询了当当的架构师团队,他们也在布置经过离线分析和大数额解析来完成,然则事实上时性和数量的准确性是一个挑衅。

 

四、Shark:分布式的MySQL分库分表中间件

5. 下一个展望

此Shark非工作流Shark,是云集首要的分布式数据访问框架,也是Java技术途径,它是一个行使程序层的数额访问框架。

  C基础框架 付出的大多了,下次再享受一个 csv文件分析内容库. 再组成一下框架 基本上用C开发小型 单机项目就没怎么难点了.

澳门美高梅手机网站 21

现在差 网络库积累 和 图形库积累,图形库 很久一个本子 内置了 glut 和 glew 后边扬弃了. 太庞大不佳, 用起来不爽. 如今一直就是

Shark的技能优点:

后端开发框架. 最终计算 对于团结那种农民工 开发库的经历就是,

  • 应有尽有的技艺文档支持;
  • 动态数据源的无缝切换;
  • 充分、灵活的分布式路由算法辅助;
  • 非proxy架构,应用直连数据库,下降外围系统器重所带来的宕机危机;
  • 工作零侵入,配置容易;
  • 站在巨人的肩膀上(springjdbc、基于druid的sqlparser已毕sql解析职责),执行质量高效、稳定;
  • 提供多机sequenceid的API帮助,解决多机sequenceid难点;
  • 缺省帮助基于zookeeper、redis3.x cluster作为集中式资源配置中央;
  • 根据velocity模板引擎渲染内容,辅助sql语句独立安排和动态拼接,与作业逻辑代码解耦;
  • 提供放置验证页面,方便开发、测试及运维人士对进行后的sql进行求证;
  • 提供自动生成配置文件的API协理,下降配置出错率;

  有怎么着想法你就去做, 有怎样错就立刻改. 有比自己更好,即刻学习 立异.

平等,也有好多限制:

再扯一点, 学编程真的没有高中的做的难,更不曾大学学的始末深, 真的一个很傻的事情, 薪俸可以, 操作机械化. 值得深搞. 

  • 不匡助强一致性的分布式事务,提议在业务层看重MQ,有限支持最终数额一致性;
  • 不提出、不协理多表查询,所有多表查询sql,务必全体打散为单条sql逐条实施;
  • sql语句的率先个参数务必是shard key;
  • shard key必须是整数类型;

 

项目的Git:https://github.com/gaoxianglong/shark,个人要支持一下,希望能继续增强改进。

后记

五、总结

  错误是难免, 有人提议来或自己发现了,一定修改!  有始有终 …

通过下面七个紧要的分布式数据访问服务/框架的可比,大家深远钻研了分布式数据访问技术,有以下收获分享给大家:

澳门美高梅手机网站 22

1. 分布式数据访问组件基于物理上的数据库分库分表,主要解决数据库的横向伸张难题

“ Monster? Is that what you think of me? I was… always care for
you! Tyrand! I thought only to prove this word in this… my power! ”

2. 业界主流的分布式数据访问组件紧要面向CRUD操作,复杂查询的完毕依靠于数据仓库、分析型数据库、大数量解析等技术来落到实处

  (“怪物?你就是这么看本身的?泰兰德?我平素在乎你!我只想透过自己的力量来能表明那一点。“)

3.
Sharding国策和SQL路由是完成分库分表的关键技术,其中Sharding的粒度:首要有二种:Database(数据库)
和 Table(表),即分库和分表

 

4. Sharding方针主要有以下三种:时间限制、ID哈希、地理区域、Hash分区、枚举分区、表明式统计分区等等

澳门美高梅手机网站,Intro: The Dawn
http://music.163.com/\#/song?id=4017240

5.
分区键的陈设相当紧要,阿里的举办值得借鉴:用户和商品八个维度,满足不相同用户的须要

另一个王 对 三元丹说

6.
分库分表后的复杂性查询是一个大的技术挑衅:须求解决跨库Join、跨多个Shard查询合并、存储进度、综合BI查询等等,可能须求引入数据仓库和大数额技术来落到实处

“ Leave this world, and never return… If you do, I’ll be waiting… ”

  1. 再有分布式事务难题要求解决,阿里有个TXC,可以学习领悟借鉴。

 (离开这些世界,永远不要再回去。即使你竟敢这么做,我会等着您……)

归咎起来说,分布式数据访问服务是大面积分布式应用的不可或缺中间件,技术复杂度和难度都很高,涉及的技能栈相比较多,同时比较深,大家会连续长远钻研,

 

对象是规划一套.Net版本的分布式数据访问服务。有趣味的伙伴,可以同步搞。

 

 

周国庆

2017/4/20

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注