雅兰亭库(yalantinglibs)介绍
雅兰亭库,名字很优雅,也很强大。它是阿里开源的一个现代C++基础工具库的集合, 现在包括 struct_pack, struct_json, struct_xml, struct_yaml, struct_pb, easylog, coro_rpc, coro_io, coro_http 和 async_simple等功能, 也一直在持续优化并添加更多的新功能。
yaLanTingLibs 的目标::为C++开发者提供高性能,极度易用的现代C++基础工具库, 帮助用户构建高性能的现代C++应用。
yalantinglibs开源地址:
https://github.com/alibaba/yalantinglibs
更多内容参见官方文档介绍:
雅兰亭库 | A collection of C++20 libraries, include async_simple, coro_rpc and struct_pack.
编译器要求
如果你的编译器只支持C++17,yalantinglibs 只会编译序列化库(struct_*系列,只能使用struct_*系列的功能如快速序列化,结构体和json的相互转换等)。如果你的编译器低于C++17,那么只围观下可以,用不了。也建议升级使用C++17及以上版本,目前来说C++17已经很常用了。
确保你的编译器版本不低于:
- clang6++ (libstdc++-8 以上)。
- g++9 或更高版本。
- msvc 14.20 或更高版本。
如果你的编译器支持C++20,yalantinglibs会编译全部库。
确保你的编译器版本不低于:
- clang11++ (libstdc++-8 以上)。
- g++10 或更高版本。
- msvc 14.29 或更高版本。
可以手动指定Cmake选项-DENABLE_CPP_20=ON
或 -DENABLE_CPP_20=OFF
来控制。
安装&编译
Yalantinglibs 是一个head-only的库,这意味着你可以简单粗暴的直接将./include/ylt
拷贝走。但是更推荐的做法还是用Cmake安装。
- 克隆仓库
git clone https://github.com/alibaba/yalantinglibs.git
-
构建,测试并安装
-
建议最好在安装之前编译样例/压测程序并执行测试:
cmake ..
cmake --build . --config debug # 可以在末尾加上`-j 选项, 通过并行编译加速
ctest . # 执行测试
测试/样例/压测的可执行文件存储在路径./build/output/
下。
- 你也可以跳过编译:
# 可以通过这些选项来跳过编译样例/压测/测试程序
cmake .. -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARK=OFF -DBUILD_UNIT_TESTS=OFF
cmake --build .
- 默认情况下会安装到系统默认的include路径,你也可以通过选项来自定义安装路径。
cmake --install . # --prefix ./user_defined_install_path
- 开始编程
- 使用CMAKE:
安装完成后,你可以直接拷贝并打开文件夹src/*/examples
,然后执行以下命令:
mkdir build
cd build
cmake ..
cmake --build .
- 手动编译:
- 将
include/
加入到头文件包含路径中(如果已安装到系统默认路径,可跳过该步骤) - 将
include/ylt/thirdparty
加入到头文件包含路径中(如果已通过Cmake 选项 -DINSTALL_INDEPENDENT_THIRDPARTY=ON 安装了第三方依赖,可跳过该步骤) - 如果你使用了
coro_
开头的任何头文件, 在linux系统下需要添加选项-pthread
. 使用g++
编译器时需要添加选项-fcoroutines
。 - 全部搞定. 更多细节请参考
example/cmakelist.txt
.
- 更多细节: 如需查看更多细节, 除了
example/cmakelist.txt
cmake中使用
前提是已经过前面步骤的构建,使用 cmake 在项目中导入 ylt 的示例:
cmake_minimum_required(VERSION 3.15)
project(file_transfer)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Threads REQUIRED)
link_libraries(Threads::Threads)
find_package(yalantinglibs REQUIRED)
link_libraries(yalantinglibs::yalantinglibs)
add_executable(coro_io_example main.cpp)
struct_pack
struct_pack是一个基于编译期反射,易用且高性能的序列化库,head-only。一行代码即可完成序列化/反序列化。性能远超protobuf等传统序列化库。
// 定义一个C++结构体
struct person {
int64_t id;
std::string name;
int age;
double salary;
};
// 初始化对象
person person1{.id = 1, .name = "hello struct pack", .age = 20, .salary = 1024.42};
// 一行代码序列化
std::vector<char> buffer = struct_pack::serialize(person1);
// 一行代码反序列化
auto person2 = deserialize<person>(buffer);
struct_pack 性能优异,最快比protobuf 高一个数量级,但是之前struct_pack 是C++20 开发的,只支持C++20 编译器,并且只支持aggregate 类型的序列化反序列化,为了让更多低版本编译器的用户也能使用struct_pack,现在支持了C++17 。
struct_json
基于反射的json库,轻松实现结构体和json之间的映射。
#include "ylt/struct_json/json_reader.h"
#include "ylt/struct_json/json_writer.h"
struct person {
std::string name;
int age;
};
REFLECTION(person, name, age);
int main() {
person p{.name = "tom", .age = 20};
std::string str;
struct_json::to_json(p, str); // {"name":"tom","age":20}
person p1;
struct_json::from_json(p1, str);
}
json 解析benchmark
struct_json 不但易用性更好,性能上也比rapidjson更高。
struct_json 解析10个文件的速度全面领先rapidjson,其中在解析gsoc-2018.json 文件的时候struct_json 性能是rapidjson 的6.3倍,说明struct_json 不仅仅在易用性和安全性上更好,性能上也不错。
struct_json, struct_xml, struct_yaml 正是为了让我们从繁琐不安全的手写代码中解放出来,通过结构体实现序列化和反序列化的自动化,安全可靠,性能还更好,是时候用它们去替代经典的rapidxxx系列啦。
coro_rpc
coro_rpc是用C++20开发的基于无栈协程和编译期反射的高性能的rpc库,在单机上echo测试qps达到2000万(详情见benchmark部分) ,性能远高于grpc和brpc等rpc库。然而高性能不是它的主要特色,coro_rpc的主要特色是易用性,免安装,包含头文件就可以用,几行代码就可以完成一个rpc服务器和客户端。
coro_rpc的设计理念是:以易用性为核心,回归rpc本质,让用户专注于业务逻辑而不是rpc框架细节,几行代码就可以完成rpc开发。 rpc的本质是什么?rpc的本质就是一个远程函数,除了rpc底层的网络IO之外,其它的就和普通函数一样。用户不需要关注rpc底层的网络IO、路由、序列化等细节,用户只需要专注于rpc函数的业务逻辑即可,这就是coro_rpc的设计理念, 正是基于这一设计理念,coro_rpc提供了非常简单易用的API给用户使用。通过一个例子来看看coro_rpc的易用性如何。
rpc使用
1.定义rpc函数
// rpc_service.hpp
inline std::string echo(std::string str) { return str; }
2.注册rpc函数和启动server
#include "rpc_service.hpp"
#include <ylt/coro_rpc/coro_rpc_server.hpp>
int main() {
// 初始化服务器
coro_rpc_server server(/*thread_num =*/10, /*port =*/9000);
server.register_handler<echo>(); // 注册rpc函数
server.start(); // 启动server并阻塞等待
}
对于rpc服务端来说,用户只需要定义rpc函数再启动server即可,不需要关注其它细节,5,6行代码就可以提供一个rpc服务了,是不是很简单!再来看看client是怎么调用hello这个rpc服务的。
rpc_client端
- 连接服务端
- rpc调用
#include "rpc_service.hpp"
#include <ylt/coro_rpc/coro_rpc_client.hpp>
Lazy<void> test_client() {
coro_rpc_client client;
co_await client.connect("localhost", /*port =*/"9000");
auto r = co_await client.call<echo>("hello coro_rpc"); //传参数调用rpc函数
std::cout << r.result.value() << "\n"; //will print "hello coro_rpc"
}
int main() {
syncAwait(test_client());
}
client调用rpc函数也同样简单,5,6行代码就可以实现rpc调用了。 就像调用本地函数一样调用远程rpc函数,在call里面输入函数名字和参数就可以实现远程调用了,非常简单。
上面的这个简单的例子已经充分展示了coro_rpc的易用性和特点了,也体现了rpc的本质,即用户可以像调用本地函数那样调用远程函数,用户只需要关注rpc函数的业务逻辑即可。
coro_rpc的接口易用性还体现在rpc函数几乎没有任何限制,rpc函数可以拥有任意多个参数,参数的序列化和反序列化由rpc库自动完成,用户无需关心。
和grpc、brpc比较易用性
rpc易用性比较
RPC | 是否需要定义DSL | 是否支持协程 | hello world例子代码行数 | 依赖库 | 是否header only |
---|---|---|---|---|---|
grpc | Yes | No | 70+ helloworld | 16 | No |
brpc | Yes | No | 40+ helloworld | 6 | No |
coro_rpc | No | Yes | 9 | 3 | Yes |
c++未来演化
如果说C++11是C++的一个里程碑,那么C++20就是现代C++的一个里程碑。因为它引入了非常重要的几个特性,而这将改变C++的编程理念和模型,当然也让C++变得更加好用,比如Concepts、Modules、Coroutine、Ranges等。以协程(Coroutine)为例,它可以让我们用同步方式写异步代码,彻底摆脱了回调地狱(Callback Hell),异步回调模型将被协程代替。用C++20新标准,失去的是弯弯绕绕的Callback,得到的是简单直接的协程。协程让异步变得简单,在未来3~5年,C++网络库的协程化将是大势所趋。
Modules让我们不用再引入include头文件了,而是像其他语言一样使用import库就好,同时它还会大大提升编译速度。将async_simple[2] 模块化后,编译速度提升了45%,性能也略有提升。可以说,Modules带来了C++统一包管理的曙光。
C++新标准的演进速度比较快,从C++11开始保持着三年一个版本的迭代速度,后面还有C++23/26/29。而未来C++标准将提供更多好东西,比如Executors、Networking、编译期反射等。虽然新标准推出比较快,但基于新标准的库却跟不上,比如协程库,目前除了async_simple外,能用的无栈协程库几乎没有,这也是C++的遗留问题。
其他资源
json与C++结构体互相转换_c++ 结构体 转 json_xyz347的博客-CSDN博客
高效工具推荐----vcpkg - 知乎
反射实现无侵入式序列化_yalantinglibs_故人帝梦的博客-CSDN博客
Github C++项目积累_xupeng1644的博客-CSDN博客
iguana反射功能 - 知乎
purecpp - a cool open source modern c++ community