比最差的API(ETW)更差的API(LTTng)是何许炼成的, 谈怎么样写一个好的接口管理

后端开发:

近期这几天在帮柠檬看她的APM系统要怎么样收集.Net运行时的种种风浪,
这么些事件包涵线程开头, JIT执行, GC触发等等.
.Net在windows上(NetFramework, CoreCLR)通过ETW(伊夫nt Tracing for
Windows), 在linux上(CoreCLR)是经过LTTng跟踪事件.

1、高级java软件架构师实战培训视频教程
2、大型SpringMVC,Mybatis,Redis,Solr,Nginx,SSM分布式电商项目摄像教程
3、斯ParkerStreaming实时流处理项目实战
4、Java校招面试 谷歌面试官亲授
5、Java开发公司级权限管理连串
6、Java大牛
带你从0到上线开发集团级电商项目
7、Java
SSM淘淘商城12天电商项目
8、Java
SSM飞速支付仿慕课网在线教育平台
9、Java
SSM开发斯Leica点评后端
10、Java
Spring带前后端支付全部电商平台
11、Java
Spring 技术栈创设前后台团购网站
12、java
Spring Security开发安全的REST服务
13、java
Spring Boot集团微信点餐系统
14、java
Spring Boot带前后端 渐进式开发集团级博客系统
15、java
c++算法与数据结构

ETW的API设计已经被不少人喝斥,
微软推出的类库krabsetw中直指ETW是最差的API再者把操作ETW的公文命名为噩梦.hpp.
而且那篇小说中, Casey
Muratori解释了为何ETW是最差的API, 原因包含:

16、ThinkPHP5.0第二季
17、ThinkPHP5.0基础视频教程
18、前端到后台ThinkPHP开发整站
19、PHP入门:基础语法到实际利用
20、PHP秒杀系统-高并发高品质的无限挑衅(完整版)
21、PHP开发高可用高安全app后端
22、php从基础到原生开发
23、PHP7+WEB+Mysql+thinkphp+laravel
24、PHP+Ajax+jQuery网站开发项目式教程
25、PHP
高质量 高价值的PHP API接口
26、PHP
thinkphp实战开发公司站
27、PHP
Thinkphp 5.0 仿百度籼米开发多集团电商平台
28、PHP
360大牛周密解读PHP面试
29、Laravel5.4快速支付简书网站
30、PHP项目之微信支付接口视频讲解
31、微信小程序入门与实战 常用组件 API 开发技术
项目实战
32、微信小程序
ThinkPHP5.0+小程序商城营造全栈应用
33、微信服务号+Yii
2.0营造商城系统全栈应用
34、Yii
2.0开发一个仿京东商城平台
35、Yii
2.0进阶版 高级组件 ES + Redis + Sentry 优化京东平台

  • 事件类型应用了位标志(最四只能够有32个), 没有设想到未来的情况
  • 分歧的接口共用一个大的构造体, 接口的输入和出口不醒目
  • 让调用者写无论怎么看都是剩下的代码
  • 让调用者使用魔法数字(而不是提供一个枚举值)
  • 命名带有误导性
  • 重临值的意义不合并
  • 选拔过度复杂, 没有先想好用例
  • 文档里面没有完全的言传身教代码, 只可以从零碎的代码拼凑

36、python_进阶强化
37、机器学习启蒙
38、Python高效编程技巧实战
39、Python操作三大主流数据库
40、python分布式爬虫打造搜索引擎
41、Python
Flask 营造微电影摄像网站
42、Vue+Django REST framework
创设清新电商项目
43、强力Django+杀手级Xadmin构建上线标准的在线教育平台
44、Python3.6
强力Django+杀手级Xadmin构建上线标准的在线教育平台
45、Python3
全网最热的Python3入门+进阶 比自学更快上手实际开发

然则Casey Muratori的篇章对自我辅助很大,
我只用了1天时间就写出了拔取ETW收集.Net运行时事件的演示代码.
从此将来我起来看哪样行使LTTng收集这么些事件,
根据自己过去的经验linux上的类库api寻常会比windows的好用, 但LTTng是个例外.

46、玩转算法
47、算法与数据结构
48、玩转算法面试 leetcode
49、看得见的算法 7个经典应用诠释算法精髓
50、网络架构原版
51、Sass
基础教程

自我第一件做的事体是去追寻怎么样在c程序里面LTTng的接口,
我打开了他们的文档然后初阶浏览.
高效自己发现了他们的文档只谈了怎么样选择代码出殡事件,
却不曾其他申明什么用代码收起事件, 我意识到我应当去看源代码.

前端开发:

初始化LTTng

选择LTTng跟踪事件首先须要成立一个会话, 启用事件和添加上下文参数,
然后启用跟踪, 在命令行里面是如此的调用:

lttng create --live
lttng enable-event --userspace --tracepoint DotNETRuntime:GCStart_V2
lttng add-context --userspace --type vpid
lttng add-context --userspace --type vtid
lttng start

lttng那一个命令的源代码在github上,
通过几分钟的检索自身意识lttng的逐条命令的完结都是保存在其一文件夹下的.
打开create.c后又发现了创建会话调用的是lttng_create_session函数,
lttng_create_session函数可以经过引用lttng.h调用.
再过了几分钟我写出了第一行代码

int ret = lttng_create_session_live("example-session", "net://127.0.0.1", 1000000);

运转后及时就报错了, 错误是”No session daemon is available”.
原因是lttng-sessiond那些程序没有启动,
lttng是经过一个单独服务来管理会话的, 而那么些服务要求手动启动.

应用独立服务本身并未错, 不过lttng-sessiond本条顺序提供了累累参数,
假若一个只想跟踪用户事件的主次启动了这些服务并指定了忽略内核事件的参数,
然后别的一个跟踪内核事件的次第将不可以健康运作.
是的的做法是利用systemd来启动那些服务, 让系统管理员决定用什么参数,
而不是让调用者去启动它.

解决那几个题材只须求简单无情的两行, 启动时若是已经起步过新进度会败北,
没有其余影响:

system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));

现在lttng_create_session_live会回来成功了, 可是又发现了新的难点,
创建的对话是由一个独门的劳务管理的, 即便当前经过退出会话也会存在,
第二次创立的时候会回到一个已存在的错误.
以此标题和ETW的标题一模一样, 解决格局也同样,
在开创会话前关闭它就可以了.

于是代码变成了如此:

system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));
lttng_destroy_session(SessionName);
int ret = lttng_create_session_live("example-session", "net://127.0.0.1", 1000000);

通过一段时间后, 我用代码达成了和命令行一样的功用:

// start processes, won't replace exists
system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));

// create new session
lttng_destroy_session(SessionName);
int ret = lttng_create_session_live(SessionName, SessionUrl, LiveSessionInterval);
if (ret != 0) {
    std::cerr << "lttng_create_session: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// create handle from session
lttng_domain domain = {};
domain.type = LTTNG_DOMAIN_UST;
lttng_handle* handle = lttng_create_handle(SessionName, &domain);
if (handle == nullptr) {
    std::cerr << "lttng_create_handle: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// enable event
lttng_event event = {};
event.type = LTTNG_EVENT_TRACEPOINT;
memcpy(event.name, EventName.c_str(), EventName.size());
event.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
event.loglevel = -1;
ret = lttng_enable_event_with_exclusions(handle, &event, nullptr, nullptr, 0, nullptr);
if (ret < 0) {
    std::cerr << "lttng_enable_event_with_exclusions: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// add context
lttng_event_context contextPid = {};
contextPid.ctx = LTTNG_EVENT_CONTEXT_VPID;
ret = lttng_add_context(handle, &contextPid, nullptr, nullptr);
if (ret < 0) {
    std::cerr << "lttng_add_context: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// start tracing
ret = lttng_start_tracing(SessionName);
if (ret < 0) {
    std::cerr << "lttng_start_tracing: " << lttng_strerror(ret) << std::endl;
    return -1;
}

到那里甘休是否很粗略? 固然尚未文档, 可是这几个api都是分外简单的api,
看源代码就能够想见怎么着调用.

52、webapp书城支付
53、组件情势支付 Web App全站
54、Web
App用组件格局支付全站

收获事件

在告知LTTng启用跟踪后, 我还亟需得到发送到LTTng的事件,
在ETW中获得事件是经过注册回调获取的:

EVENT_TRACE_LOGFILE trace = { };
trace.LoggerName = (char*)mySessionName.c_str();
trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)(StaticRecordEventCallback);
trace.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)(StaticBufferEventCallback);
trace.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME;
TRACEHANDLE sessionHandle = ::OpenTrace(&trace);
if (sessionHandle == INVALID_PROCESSTRACE_HANDLE) {
    // ...
}
ULONG processStatus = ::ProcessTrace(&sessionHandle, 1, nullptr, nullptr);

自我寻思lttng有没有那样的体制,
首先我见状的是lttng.h中的lttng_register_consumer函数,
那一个函数的笺注如下:

This call registers an "outside consumer" for a session and an lttng domain.
No consumer will be spawned and all fds/commands will go through the socket path given (socket_path).

翻译出来就是给会话注册一个表面的买主, 听上去和自我的渴求很像吧?
其一函数的第四个参数是一个字符串, 我想来是unix socket, lttng会通过unix
socket发送事件过来.
于是乎我写了这么的代码:

ret = lttng_register_consumer(handle, "/tmp/custom-consumer");

一执行及时报错, 错误是Command undefined, 也就是命令未定义,
服务端不援救那个命令.
通过查找发现lttng的源代码中尚无此外调用这几个函数的位置,
也就是说那么些函数是个装饰.
看起来这几个方法行不通.


经过一番追寻,
我意识了live-reading-howto那么些文档,
里面的情节万分少不过可以看来使用lttng-relayd以此服务能够读取事件.
读取事件方今只支持TCP, 使用TCP传输事件数量不仅复杂而且功用很低,
相对ETW直接通过内存传递数据那确实是个鲁钝的办法.
即使愚钝可是依旧要持续写, 我起来看那TCP传输用的是何许协议.

对传输协议的分解文档在live-reading-protocol.txt,
那篇文档写的很糟糕, 但总比没有好.
lttng-relayd进展互动使用的是一个lttng自己创立的半双工二进制协议,
设计如下:

客户端发送命令给lttng-relayd亟需遵守以下的格式

[data_size: unsigned 64 bit big endian int, 命令体大小]
[cmd: unsigned 32 bit big endian int, 命令类型]
[cmd_version: unsigned 32 bit big endian int, 命令版本]
[命令体, 大小是data_size]

出殡命令的规划不成难题, 一大半二进制协议都是这般设计的,
难题在于接收命令的设计.
接过命令的格式完全看重于发送命令的项目,
例如LTTNG_VIEWER_CONNECT其一命令发送过去会收到以下的数额:

[viewer_session_id: unsigned 64 bit big endian int, 服务端指定的会话ID]
[major: unsigned 32 bit big endian int, 大版本]
[minor: unsigned 32 bit big endian int, 中版本]
[type: 客户端的类型]

可以看来接收的多寡尚无数据头, 没有数据头如何决定收取多少数量吧?
那就要求客户端定义的答疑大小必须和服务端完全一致, 一个字段都不可以漏.
服务端在将来的翻新中无法给再次来到数据随意添加字段,
再次来到多少字段必要取决于发送过来的cmd_version,
保持api的包容性将会丰富的麻烦.
目前在lttng中cmd_version是一个留下字段,
也就是她们从没仔细的想过api的更新难点.
没错的做法应该是再次回到数据也应有提供一个数据头,
然后同意客户端忽略多出去的数据.


看完协议未来, 我在想既然使用了二进制协议,
应该也会提供一个sdk来压缩解析的工作量吗?
经过一番摸索找到了一个头文件lttng-viewer-abi.h,
包蕴了和lttng-relayd互相使用的数据结构体定义.
那么些头文件在源代码里面有, 不过却不在LTTng发表的软件包中,
那表示使用它必要复制它到项目里面.
复制旁人的源代码到项目里面不可能那么不论是,
看了一晃LTTng的开源协议,
include/lttng/*src/lib/lttng-ctl/*下的公文是LGPL,
其他文件是GPL,
也就是地方一旦把这几个头文件复制到自己的花色里面,
自己的档次必须利用GPL协议开源
,
不想用GPL的话只可以把内部的始末自己一行行重新写, 还无法写的太像.

既然是测试就不管那样多了, 把那些头文件的代码复制过来就从头持续写,
首先是连接受lttng-relayd:

int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
    perror("socket");
    return -1;
}
sockaddr_in address = {};
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_family = AF_INET;
address.sin_port = htons(5344);
ret = connect(fd, (sockaddr*)&address, sizeof(address));
if (ret < 0) {
    perror("connect");
    return -1;
}

连日来成功之后的相互流程在阅读方面的商议文档未来可以整理如下:

初始化
    客户端发送命令 LTTNG_VIEWER_CLIENT_COMMAND + 构造体 lttng_viewer_connect
    服务端返回构造体 lttng_viewer_connect
    客户端发送命令 LTTNG_VIEWER_CREATE_SESSION + 构造体 lttng_viewer_create_session_response
    服务端返回构造体 lttng_viewer_create_session_response
列出会话
    客户端发送命令 LTTNG_VIEWER_LIST_SESSIONS, 不带构造体
    服务端返回构造体 lttng_viewer_list_sessions + 指定长度的 lttng_viewer_session
附加到会话
    客户端发送命令 LTTNG_VIEWER_ATTACH_SESSION + 构造体 lttng_viewer_attach_session_request
    服务端返回构造体 lttng_viewer_attach_session_response + 指定长度的 lttng_viewer_stream
循环 {
    如果需要获取新的流 {
        客户端发送命令 LTTNG_VIEWER_GET_NEW_STREAMS + 构造体 lttng_viewer_new_streams_request
        服务端返回构造体 lttng_viewer_new_streams_response + 指定长度的 lttng_viewer_stream
    }
    如果需要获取新的元数据(metadata) {
        枚举现存的metadata流列表 {
            客户端发送命令 LTTNG_VIEWER_GET_METADATA + 构造体 lttng_viewer_get_metadata
            服务端返回构造体 lttng_viewer_metadata_packet + 指定长度的payload
        }
    }
    枚举现存的trace流列表 {
        客户端发送命令 LTTNG_VIEWER_GET_NEXT_INDEX + 构造体 lttng_viewer_get_next_index
        服务端返回构造体 lttng_viewer_index
        检查返回的 index.flags, 如果服务端出现了新的流或者元数据, 需要先获取新的流和元数据才可以继续
        客户端发送命令 LTTNG_VIEWER_GET_PACKET + 构造体 lttng_viewer_trace_packet
        服务端返回构造体 lttng_viewer_trace_packet + 指定长度的payload
        根据metadata packet和trace packet分析事件的内容然后记录事件
    }
}

是或不是认为很复杂?
因为协议决定了服务端发给客户端的数量没有数据头,
所以服务端不可能主动推送数据到客户端, 客户端必须主动的去举办轮询.
假设您放在心上到构造体的称号,
会发现有些构造体后边有request和response而有的没有,
要是不看上下文只看构造体的称谓很难猜到它们的作用.
科学的做法是有所请求和再次回到的布局体名称末尾都添加request和response,
不要去大致这几个字母而浪费思考的时间.


为了发送命令和接收构造体我写了一些扶助函数, 它们并不复杂,
使用TCP交互的主次都会有近似的代码:

int sendall(int fd, const void* buf, std::size_t size) {
    std::size_t pos = 0;
    while (pos < size) {
        auto ret = send(fd,
            reinterpret_cast<const char*>(buf) + pos, size - pos, 0);
        if (ret <= 0) {
            return -1;
        }
        pos += static_cast<std::size_t>(ret);
    }
    return 0;
}

int recvall(int fd, void* buf, std::size_t size) {
    std::size_t pos = 0;
    while (pos < size) {
        auto ret = recv(fd,
            reinterpret_cast<char*>(buf) + pos, size - pos, 0);
        if (ret <= 0) {
            return -1;
        }
        pos += static_cast<std::size_t>(ret);
    }
    return 0;
}

template <class T>
int sendcmd(int fd, std::uint32_t type, const T& body) {
    lttng_viewer_cmd cmd = {};
    cmd.data_size = htobe64(sizeof(T));
    cmd.cmd = htobe32(type);
    if (sendall(fd, &cmd, sizeof(cmd)) < 0) {
        return -1;
    }
    if (sendall(fd, &body, sizeof(body)) < 0) {
        return -1;
    }
    return 0;
}

初步化连接的代码如下:

lttng_viewer_connect body = {};
body.major = htobe32(2);
body.minor = htobe32(9);
body.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND);
if (sendcmd(fd, LTTNG_VIEWER_CONNECT, body) < 0) {
    return -1;
}
if (recvall(fd, &body, sizeof(body)) < 0) {
    return -1;
}
viewer_session_id = be64toh(body.viewer_session_id);

前边的代码相比较平淡我就大致了,
想看完整代码的可以看这里.


进去循环后会从lttng-relayd赢得三种有效的数目:

  • 元数据(metadata), 定义了跟踪数据的格式
  • 盯住数据(trace), 包罗了事件新闻例如GC起首和终结等

收获元数据利用的是LTTNG_VIEWER_GET_METADATA命令,
获取到的元数据内容如下:

Wu@"Jtf@oe/* CTF 1.8 */

typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
typealias integer { size = 27; align = 1; signed = false; } := uint27_t;

trace {
    major = 1;
    minor = 8;
    uuid = "a3df4090-0722-4a74-97a4-81e066406f03";
    byte_order = le;
    packet.header := struct {
        uint32_t magic;
        uint8_t  uuid[16];
        uint32_t stream_id;
        uint64_t stream_instance_id;
    };
};

env {
    hostname = "ubuntu-virtual-machine";
    domain = "ust";
    tracer_name = "lttng-ust";
    tracer_major = 2;
    tracer_minor = 9;
};

clock {
    name = "monotonic";
    uuid = "f397e532-4837-402b-8cc9-700ed92a339d";
    description = "Monotonic Clock";
    freq = 1000000000; /* Frequency, in Hz */
    /* clock value offset from Epoch is: offset * (1/freq) */
    offset = 1514336042565610080;
};

typealias integer {
    size = 27; align = 1; signed = false;
    map = clock.monotonic.value;
} := uint27_clock_monotonic_t;

typealias integer {
    size = 32; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint32_clock_monotonic_t;

typealias integer {
    size = 64; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint64_clock_monotonic_t;

struct packet_context {
    uint64_clock_monotonic_t timestamp_begin;
    uint64_clock_monotonic_t timestamp_end;
    uint64_t content_size;
    uint64_t packet_size;
    uint64_t packet_seq_num;
    unsigned long events_discarded;
    uint32_t cpu_id;
};

struct event_header_compact {
    enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
    variant <id> {
        struct {
            uint27_clock_monotonic_t timestamp;
        } compact;
        struct {
            uint32_t id;
            uint64_clock_monotonic_t timestamp;
        } extended;
    } v;
} align(8);

struct event_header_large {
    enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
    variant <id> {
        struct {
            uint32_clock_monotonic_t timestamp;
        } compact;
        struct {
            uint32_t id;
            uint64_clock_monotonic_t timestamp;
        } extended;
    } v;
} align(8);

stream {
    id = 0;
    event.header := struct event_header_compact;
    packet.context := struct packet_context;
    event.context := struct {
        integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vpid;
        integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vtid;
    };
};

event {
    name = "DotNETRuntime:GCStart_V2";
    id = 0;
    stream_id = 0;
    loglevel = 13;
    fields := struct {
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Count;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Depth;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Reason;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Type;
        integer { size = 16; align = 8; signed = 0; encoding = none; base = 10; } _ClrInstanceID;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _ClientSequenceNumber;
    };
};

以此元数据的格式是CTF Metadata,
这些格式看上去像json然则并不是, 是LTTng的店堂自己创办的一个文本格式.
babeltrace中带有了分析那些文本格式的代码,
不过没有开摒弃何解析它的接口,
也就是若是你想协调分析只可以写一个词法分析器.
这个格式其实可以利用json表示, 体积不会大增多少,
不过那公司就是发明了一个新的格式增添使用者的负担.
写一个词法分析器必要1天时间和1000行代码, 那里我就先跳过了.


接下去获取跟踪数据,
使用的是LTTNG_VIEWER_GET_NEXT_INDEX和LTTNG_VIEWER_GET_PACKET命令.
LTTNG_VIEWER_GET_NEXT_INDEX再次来到了当前流的offset和可得到的content_size,
这里的content_size单位是位(bit),
也就是需求除以8才足以算出可以拿到多少字节,
关于content_size的单位LTTng中没有任何文档和注释表达它是位,
唯有一个测试代码里面的某行写了/ CHAR_BIT.
使用LTTNG_VIEWER_GET_PACKET命令,
传入offset和content_size/8可以得到跟踪数据(倘使不/8会获得到剩余的数据依旧重回ERR).
实质上重回的跟踪数据如下:

000000: c1 1f fc c1 29 82 6b fe 24 10 4c 6b 97 91 4d c3  ....).k.$.Lk..M.
000010: ed d4 41 8f 00 00 00 00 03 00 00 00 00 00 00 00  ..A.............
000020: 92 91 49 96 08 0a 00 00 07 a0 58 b9 08 0a 00 00  ..I.......X.....
000030: 50 05 00 00 00 00 00 00 00 80 00 00 00 00 00 00  P...............
000040: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000050: 03 00 00 00 1f 00 00 00 00 92 91 49 96 08 0a 00  ...........I....
000060: 00 e1 1b 00 00 03 00 00 00 02 00 00 00 01 00 00  ................
000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1f  ................
000080: 00 00 00 00 4d ae a7 af 08 0a 00 00 e1 1b 00 00  ....M...........
000090: 04 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00  ................
0000a0: 00 00 00 00 00 00 00 00 00 00                    ..........

钉住数据的格式是CTF Stream
Packet
,
也是一个自定义的二进制格式, 必要协作元数据解析.
babeltrace中一样没有开放解析它的接口(有python
binding不过尚未解析数据的函数), 也就是索要协调写二进制数据解析器.

操作LTTng + 和relayd通信 + 元数据词法分析器 +
跟踪数据解析器全体加起来估计必要2000行代码,
而那所有应用ETW只用了100多行代码.
不佳的陈设性, 复杂的应用, 落后的文档, 种种各类的自定义协议和数码格式,
不提供SDK把LTTng打造成了一个比ETW更难用的跟踪系统.
眼下在github上LTTng唯有100多星而babeltrace唯有20多,
也作证了从未稍微人在用它们.
自身不知底为何CoreCLR要用LTTng, 但欣慰的是CoreCLR
2.1会有新的跟踪机制EventPipe,
到时候可以更简短的贯彻跨平台捕获CoreCLR跟踪事件.

自家当下写的调用ETW的代码放在了这里,
调用LTTng的代码放在了这里,
有趣味的能够去参考.

55、vue2.0智能社
56、vue.js零基础
57、全网稀缺Vue 2.0尖端实战
独立开发专属音乐WebAPP
58、Vue、Node、MongoDB高级技术栈全覆盖
59、vue2.0带你入门Vue 2.0及案例开发
60、Vue2.0+Node.js+MongoDB 营造商城系统
61、Vue.js高仿饿了么外卖App 前端框架Vue.js
1.0升格2.0
62、Vue+Django REST framework
打造清新电商项目

教训

最差的API(ETW)和更差的API(LTTng)都看过了, 那么相应怎么样幸免他们的谬误,
编写一个好的API呢?

Casey
Muratori
关系的教训有:

63、node.JS
线上服务器陈设
64、Node.js微信公众号支付
65、NodeJs实战+mongodb+聊天系统
66、全栈最终一公里 – Nodejs
项目标线上服务器安插与发布

规划API的第一条和第二条规则: “永远都从编写用例先导”

规划一个API时, 首先要做的是站在调用者的立场, 想想调用者需求哪些,
怎样才能最不难易行的直达这一个须要.
编写一个简短的用例代码永远是统筹API中务必的一步.
不要过多的去想内部贯彻, 若是内部贯彻机制让API变得复杂,
应该想艺术去抽象它.

67、React框架课程套装
68、贯穿全栈React Native开发App
69、React.js福特点评案例完整版
70、React
Native开发跨平台Github App
71、React
Native开发App狗狗说
72、React
native 火速开发App
73、React.js入门与实战
开发适配PC端及活动端新闻头条平台

考虑到未来的扩大

因为需要会不断变化, 设计API的时候理应为以后的变更预留空间,
有限协理向后相当性.
例如ETW中监听的风浪类型.aspx)使用了位标记,
也就是参数是32位时最三只可以有32种事件,
考虑到未来有更多事件应该把事件类型定义为总是的数值并提供额外的API启用事件.
前天有很多接口在设计时会考虑到版本, 例如用v1和v2区分,
那是一个很好的策略.

74、ionic2连忙上手的跨平台App开发
75、Angular单页应用 仿拉钩
76、AngularJS全栈开发和讯
77、Angular
创设集团级合营平台  288
78、Angular
4.0从入门到实战 打造股票管理网站  199

备受关注接口的输入和输出

决不为了省去代码去让一个接口接收或者重返多余的音讯.
在ETW中过多接口都共用了一个大构造体EVENT_TRACE_PROPERTIES,
调用者很难搞驾驭接口使用了构造体里面的怎样值, 又影响了如何值.
统筹API时应该显明接口的目标, 让接口接收和重临必要且最少的音信.

79、前端小白入门课程
80、前端
一往无前的响应式开发
81、响应式开发一招致胜
82、前端跳槽面试必备技巧
83、JavaScript面试技巧全套
84、ES6零基础教学解析彩票品类
85、手把手从0创设集团级电商平台
86、腾讯大牛教你web前后端漏洞分析与防卫
87、对接真实数据
从0开发前后端分离集团级上线项目
88、Javascript 让您页面速度飞起来 –
Web前端品质优化

提供全部的演示代码

对调用者来说, 100行的以身作则代码平日比1000行的文档更有意义.
因为接口的设计者和调用者拥有的知识量寻常不对等,
调用者在并未看出实际的例证从前, 很可能仍然不能够清楚设计者编写的文档.

移步端支付:

永不选取魔法数字

那是成百上千接口都会犯的谬误, 例如ETW中决定事件附加的音信时, 1代表时间戳,
2表示系统时间, 3表示CPU周期计数.
即使您须求传递具有某种意义的数字给接口,
请务必在SDK中为该数字定义枚举类型.

本人从LTTng中接到到的训诫有:

89、贯穿全栈React Native开发App
90、React
Native开发跨平台Github App
91、React
native 快速支付轻量级App

写文档

99%的调用者没有看源代码的趣味或者能力,
不写文档没有人会驾驭怎么着去调用你的接口.
如今有众多自动生成文档的工具, 用这几个工具得以减掉过多的工作量,
可是您照样应该手动去编写一个入门的文档.

92、零基础入门安卓与界面
93、基于okhttp 3 的 Android
互联网层架构设计实战
94、指导新手急迅开发Android App完整版
95、Kotlin系统入门与进阶
96、Android自动化测试-java篇
97、Android专项测试-Python篇
98、Android通用框架设计与完整电商APP开发
99、Android应用发展趋势必备武器
热修复与插件化
100、Android常用框架教程Retrofit2 OKhttp3 Dagger2
RxJava2

无须随意的去成立一个共谋

创办一个新的商议表示须要编制新的代码去分析它,
而且每个程序语言都要再一次编辑三回.
唯有您很有生机, 可以为主流的程序语言都提供一个SDK, 否则不推荐那样做.
重重档次都提供了REST API, 那是很好的自由化,
因为大概每个语言都有现成的类库可以便宜地调用REST API.

数据库:

当心的去定义二进制协议

概念一个好的二进制协议须要很深的造诣, LTTng定义的商议明显考虑的太少.
引进的做法是妇孺皆知区分请求和回复, 请求和回复都应有有一个涵盖长度的头,
帮衬全双工通信.
若是您想设计一个二进制协议,
强烈提出参考Cassandra数据库的合计文档,
那几个协议无论是设计照旧文档都是一等的水平.
不过若是您未曾对传输质量有很苛刻的要求, 指出使用现成的商谈加json或者xml.

103、MySQL品质管理及架构设计
104、创设扛得住的MySQL数据库架构
105、Redis从入门到高可用,分布式实践
106、高质量MySql 可伸张MySQL数据库设计及架构优化
电商项目

不要去创制一个DSL(Domain-specific language)

那边自己从不写轻易, 如若你有一个数据结构要求代表成文本,
请使用更通用的格式.
LTTng表示元数据时选拔了一个自己创设的DSL,
但里面的内容用json表示也不会追加多少体积,
也就是说成立一个DSL没有其余好处.
解析DSL要求协调编辑词法分析器,
就算是经验老到的程序员编写一个也急需多多时辰(包含单元测试更加多),
如果使用json等通用格式那么编写解析的代码只要求几分钟.

Linux:

写在最终

即便那篇小说把LTTng批评了一番,
但那或许是眼前海内外唯一一篇涉嫌怎么着通过代码调用LTTng和收受事件数量的小说.
期待看过那篇小说的安插性API时多为调用者着想,
你偷懒省下几分钟往往会导致别人浪费几天的时间.

107、急迅上手Linux 玩转典型应用
108、Nginx
公司级刚需Nginx入门

事在人为智能/大数据:

109、深度学习
110、机器学习
111、10时辰入门大数量
112、Hadoop大数据零基础实战培训课程唯一高清完整版第一季

详情请 咨询  QQ    759104513

Post Author: admin

发表评论

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