正如最差的API(ETW)更不比之API(LTTng)是怎样炼成的, 谈如何勾勒一个好的接口

后端开发:

最近立几乎龙在扶柠檬看其底APM系统一旦什么样收集.Net运行时的各种风波,
这些事件包括线程开始, JIT执行, GC触发等等.
.Net于windows上(NetFramework, CoreCLR)通过ETW(Event Tracing for
Windows), 在linux上(CoreCLR)是经LTTng跟踪事件.

1、高级java软件架构师实战培训视频教程
2、大型SpringMVC,Mybatis,Redis,Solr,Nginx,SSM分布式电商项目视频教程
3、Spark
Streaming实时流处理项目实战
4、Java校招面试 Google面试官亲授
5、Java开发公司级权限管理网
6、Java大牛
带您从0到上线开发公司级电商项目
7、Java
SSM淘淘商城12天电商项目
8、Java
SSM快速开仿慕课网在线教育平台
9、Java
SSM开发大众点评后端
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