{"id":17241202,"url":"https://github.com/laruence/yar-c","last_synced_at":"2025-04-14T03:13:07.221Z","repository":{"id":18641678,"uuid":"21848240","full_name":"laruence/yar-c","owner":"laruence","description":"Yar C Framework","archived":false,"fork":false,"pushed_at":"2021-06-29T05:57:21.000Z","size":1050,"stargazers_count":108,"open_issues_count":5,"forks_count":29,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-04-14T03:12:49.489Z","etag":null,"topics":["c","rpc","yar","yar-server"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/laruence.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-15T05:58:29.000Z","updated_at":"2025-03-19T08:13:15.000Z","dependencies_parsed_at":"2022-09-15T01:21:33.477Z","dependency_job_id":null,"html_url":"https://github.com/laruence/yar-c","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laruence%2Fyar-c","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laruence%2Fyar-c/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laruence%2Fyar-c/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laruence%2Fyar-c/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/laruence","download_url":"https://codeload.github.com/laruence/yar-c/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248813801,"owners_count":21165634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","rpc","yar","yar-server"],"created_at":"2024-10-15T06:08:11.989Z","updated_at":"2025-04-14T03:13:07.173Z","avatar_url":"https://github.com/laruence.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Yar C Framework\n[![Build Status](https://secure.travis-ci.org/laruence/yar-c.png)](https://travis-ci.org/laruence/yar-c)\n\n(see also: [Yar PHP framework](https://github.com/laruence/yar), [Yar Java framework](https://github.com/weibocom/yar-java))\n## Requirement\n- libevent\n- [msgpack](https://github.com/msgpack/msgpack-c)\n\n## Install\n\n````\n./configure --with-msgpack=/path-to-msgpack --with-event=/path-to-libevent\nmake \n`````\n\n## Example\n\nyou can find a example in example folder\n\n## Manual\n\n### Example\n\n 当Yar成功安装以后, example目录下有一个简单的例子, 会有助于学习基于Yar的开发.\n\n### 服务端编程API\n\n 如果你是要基于Yar for C开发一个C服务, 那么Yar\\_Server是你要关心的API, Yar for C(以下简称Yar), 它基于libevent和msgpack, 为开发者提供了daemon, pre-fork, socket manipulation, logging, pack/unpack等作为一个Server常用的功能.\n\n#### yar\\_server\\_init\n\n````c\n int yar_server_init(char *hostname);\n````\n\n 初始化Yar\\_Server, 参数为一个字符串的监听地址, 比如对于IPV4来说类似:localhost:8888, 127.0.0.1:8888, 必须包含端口号.\n\n 对于Unix domain socket来说, 类似: /tmp/yar.sock\n\n 如果成功返回1, 失败返回0.\n\n 我们也能看到, 并没有Server实例返回, 也就是说, 一个进程只能存在一个Server实例. 这点要注意.\n\n#### yar\\_server\\_set\\_opt\n\n````c\n int yar_server_set_opt(yar_server_opt opt, void *val);\n````\n\n 设置Server的参数, 可选的参数有:\n\n````c\ntypedef enum _yar_server_opt {\n\n  YAR_STAND_ALONE,  //是否单例启动, 一般用作调试的时候, 不会fork worker\n\n  YAR_READ_TIMEOUT,  //读取请求的超时值\n\n  YAR_PARENT_INIT,   //父进程初始化Hook\n\n  YAR_CHILD_INIT,    //Woker进程初始化Hook\n\n  YAR_CHILD_USER,   //Woker运行的用户名\n\n  YAR_CHILD_GROUP,  //Woker运行的group名\n\n  YAR_CUSTOM_DATA,  //回调函数自定义参数\n\n  YAR_MAX_CHILDREN, //pre-fork的worker数目, 必须在1~128之间,\n\n                    //一般设置为和CPU核数相同即可\n\n  YAR_PID_FILE,    //PID文件的产生路径, 默认为空\n\n  YAR_LOG_LEVEL,   //LOG的级别, 默认为ALL\n\n  YAR_LOG_FILE    //日志文件, 日志文件可以为普通文件, 也可以是ronolog\n\n} yar_server_opt;\n````\n\n对于不同的选项, val应该是选项的一级指针, 比如, 设置timeout\n\n````c\n #include \"yar.h\"\n\n Int timeout = 5;\n\n yar_server_setopt(YAR_READ_TIMEOUT, \u0026timeout);\n````\n \n\n成功返回1, 失败返回0\n\n##### YAR\\_STAND\\_ALONE\n\n 是否以单进程启动, 一般用在调试的时候, 因为默认的yar server会以daemon, 并且prefork一些子进程出来, 不利于开发调试. \n\n##### YAR\\_READ\\_TIMEOUT, \n\n请求和处理的超时值, 默认为5s\n\n##### YAR\\_PARENT\\_INIT \u0026 YAR\\_CUSTOM\\_DATA\n\nYar server会prefork一些子进程, 这个hook容许我们在yar server prefork完成以后, 对于master进程会做一些初始化的工作, 如果我们设置了这个值, 那么yar在做master进程的初始化工作之后, 也会在master进程调用这个hook, 以方便我们做一些只有master进程需要做的初始化工作.\n\n 这个方法的原型是:\n\n````\n typedef void (*yar_init) (void *data);\n````\n\n 这里要关心的就data, data是一个void\\* 指针, 如果我们通过YAR\\_CUSTOM\\_DATA设置过一个自定义的数据, 那么这个指针就是我们最初设置的这个数据的指针.\n\n 主要用作在整个yar\\_server运行过程中, 传递一些我们自定义的数据.\n\n##### YAR\\_CHILD\\_INIT\n\n 同上, 不过是worker进程初始化的时候被调用. 函数原型也和parent init一样. 也支持自定义数据.\n\n##### YAR\\_CHILD\\_USER \u0026 YAR\\_CHILD\\_GROUP\n\n 如果设置了, 那么我们的worker进程就会尝试setuid/setgid到这个用户/组运行.\n\n##### YAR\\_MAX\\_CHILDREN\n\n 要prefork的子进程数目, 一般设置为和CPU的核数相当即可.\n\n##### YAR\\_LOG\\_FILE \u0026 YAR\\_LOG\\_LEVEl\n\n Yar server默认的时候会输出一些日志信息, 但是当yar以正常模式启动的时候, 会以daemon模式运行, 这样一来就无法输出日志信息到stderr/stdout.\n\n 所以, 需要设置一个文件/管道日志输出目的地.\n\n 这个选项支持文件, 或者管道, 对于文件自然没有什么好说, 对于管道的话, 主要结合cronolog来使用.\n\n比如:\n\n````\n \"|/home/huixinchen/local/cronolog/sbin/cronolog ./yar_server_%M.log\"\n````\n\n就是说, 日志输出到当前目录的yar\\_server\\_\\*.log, \\*是当前分钟, 也就是说日志会以分钟做分割.\n\n 而对于log level来说, Yar 分为YAR\\_DEBUG, YAR\\_NOTICE, YAR\\_WARN, YAR\\_ERROR 5个级别的日志级别.\n\n 当要输出日志的级别大于log level则输出, 否不输出, 所以这个选项是用作过滤输出的.\n\n#### yar\\_server\\_get\\_opt\n\n````\nvoid * yar_server_get_opt(yar_server_opt opt);\n````\n\n获取某个选项的值\n\n成功返回指针的抽象指针, 失败返回NULL\n\n#### yar\\_server\\_register\\_handler\n\n````c\nint yar_server_register_handler(yar_server_handler *handlers);\n````\n\n注册一个服务函数, 服务函数的原型是:\n\n````c\ntypedef void (*yar_handler) (yar_request *request, yar_response *response, void *data);\n````\n\n而, yar\\_server\\_handler的定义是:\n\n````c\ntypedef struct _yar_server_handler {\n\n  char *name;\n\n  int len;\n\n  yar_handler handler;\n\n} yar_server_handler;\n````\n\n其中name就是RPC调用的时候的方法名, 比如:\n\n````c\nyar_server_handler example_handlers[] = {\n\n  {\"default\", sizeof(\"default\") - 1, yar_handler_example},\n\n  {NULL, 0, NULL}\n\n};\n````\n\n那么当客户端的RPC请求default方法的时候, yar\\_handler\\_example就会被调用, 去处理这个请求. 以PHP客户端为例:\n\n````php\n\u003c?php\n   $yar = new Yar_Client(“tcp://127.0.0.1”);\n   $yar-\u003edefault($args); // yar_handler_example会处理该请求\n?\u003e\n````\n\n成功返回1, 失败返回0\n\n#### yar\\_server\\_run\n\n````c\nint yar_server_run();\n````\n\n开始运行Server, 这个调用将会开始pre-fork, listening, accpt, process流程.\n\n除非Server被shutdown, 否则这个函数不会返回.\n\n#### yar\\_server\\_shutdown\n\n````c\nvoid yar_server_shutdown();\n````\n\n关闭Server, 这个会停止accpt新请求, worker会在处理完当前请求以后退出\n\n#### yar\\_server\\_destroy\n\n````c\nvoid yar_server_destroy();\n````\n\n销毁Server\n\n### 客户端编程API\n\n如果, 你是希望请求一个已有的Yar\\_Server, 那么Yar\\_Client是你要关心的, 它会请求一个Yar\\_Server, 并且得到返回.\n\n#### yar\\_client\\_init\n\n````c\nyar_client * yar_client_init(char *hostname);\n````\n\n实例化一个Yar\\_Client, 参数是目的地址\n\n成功返回Yar\\_Client实例:\n\n````c\nstruct _yar_client {\n\n  int fd;\n\n  char *hostname;\n\n  yar_client_call call;\n\n};\n````\n\n一般的, 我们不用关心这个结构体的内容, 只需要关心yar\\_client\\_call的原型:\n\n````c\ntypedef yar_response * (*yar_client_call)(yar_client *client, char *method, uint num_args, yar_packager *packager[]);\n````\n\n也就是说, 当得到一个Client实例以后, 我们就可以对Server发起调用, 比如我们调用Server的default方法, 并且有2个参数, 那么就类似\n\n````c\nyar_client *client = yar_client_init(\"tcp://localhost:2222\");\nyar_response *response = client-\u003ecall(client, \"default\", 2, args);\n````\n\n如果, 失败返回NULL, 比如Server不能连接等.\n\n#### yar\\_client\\_destroy\n\n````c\nvoid yar\\_client\\_destroy(yar\\_client \\*client);\n````\n\n调用完成后, 销毁一个Yar\\_Client实例\n\n### 参数和返回值\n\n Yar采用msgpack作为打包协议, 并且为开发者封装了一系列简单的API来实现对数据的打包解包\n\n#### 解包的相关API\n\n 观察之前的\n\n````c\ntypedef void (*yar_handler) (yar_request *request, yar_response *response, void *data),\n````\n\n 我们看到, 当请求到来的时候, 我们Server端注册的处理函数被调用, 其中俩个参数分别为yar\\_request, 和yar\\_response.\n\n 这个时候, 我们首先要关心的是客户端调用传来了几个参数, 参数分别是什么, 参数的信息保存在request-\u003ein里面, 它是个yar\\_data 指针.\n\n 在这里我们不用关心yar\\_data的结构体的组成是什么, 因为我们只需要调用一系列API就可以得到参数.\n\n Yar协议规定, 所有的参数都打包在一个数组里面, 所以request-\u003ein是一个数组.\n\n 于是, 如果我要检查当前的参数个数, 那么就调用:\n\n````c\nyar_data_type yar_unpack_data_type(const yar_data *data, uint *size);\n````\n\n其中, yar\\_data\\_type是一个unum, 可选值是:\n\n````c\ntypedef enum _yar_data_type {\n\n  YAR_DATA_NULL = 1,\n\n  YAR_DATA_BOOL,\n\n  YAR_DATA_LONG,\n\n  YAR_DATA_ULONG,\n\n  YAR_DATA_DOUBLE,\n\n  YAR_DATA_STRING,\n\n  YAR_DATA_MAP,\n\n  YAR_DATA_ARRAY\n\n} yar_data_type;\n````\n\n对于, string, map, 和array, yar\\_unpack\\_data\\_type的第一个参数将会返回他们的长度或者是元素个数, 比如对于\n\n````\nmap {'k' =\u003e 'v'},\n````\n\n那么. 返回的size是1.\n\n现在我们就知道怎么检查参数个数了吧, 假设我们的例子只接受3个参数\n\n````c\nuint size = 0;\n\nif (yar_unpack_data_type(request-\u003ein, \u0026size) != YAR_DATA_ARRAY || size != 3) {  \n\tyar_response_set_error(response, YAR_ERROR, \"参数检查失败, 只接受3个参数\");\n  \treturn ;\n}\n````\n\n假设我们现在参数已经检查通过, 假设我们接受2个参数, 分别是俩个整数.\n\n那么就通过如下形式获得相关参数内容:\n\n````c\nuint arg[2], dummy;\n\nyar_data *tmp;\n\nconst yar_data *parameters = yar_request_get_parameters(request);\n\nyar_unpack_iterator *it = yar_unpack_iterator_init(parameters); //生成迭代器\n\nint index = 0;\n\ndo {\n\n tmp = yar_unpack_iterator_current(it);\n\n if (yar_unpack_data_type(tmp, \u0026dummy) != YAR_DATA_LONG) {\n\n  yar_response_set_error(response, YAR_ERROR, \"参数检查失败, 只接受整数\");\n\n  return ;\n\n }\n\n \n\n arg[index++] = *(long *)( yar_unpack_data_value(tmp));\n\n} while(yar_unpack_iterator_next(it));\n````\n\n这样我们的arg就得到了俩个整数参数.\n\n#### 打包的相关API\n\n 当我们获得参数, 并且处理完请求以后, 我们需要返回数据给客户端, 这个时候我们就需要和打包的API打交道了. 他们是:\n\n````c\nint yar_pack_push_array(yar_packager *packager, uint size);\n\nint yar_pack_push_map(yar_packager *packager, uint size);\n\nint yar_pack_push_null(yar_packager *packager);\n\nint yar_pack_push_bool(yar_packager *packager, int val);\n\nint yar_pack_push_long(yar_packager *packager, long num);\n\nint yar_pack_push_ulong(yar_packager *packager, ulong num);\n\nint yar_pack_push_double(yar_packager *packager, double num);\n\nint yar_pack_push_string(yar_packager *packager, char *str, uint len);\n\nint yar_pack_push_data(yar_packager *packager, yar_data *data);\n\nint yar_pack_push_packager(yar_packager *packager, yar_packager *data);\n\nint yar_pack_to_string(yar_packager *packager, yar_payload *payload);\n\nvoid yar_pack_free(yar_packager *packager);\n````\n\n不要看函数很多, 但其实很简单. 打包的时候, 是一唯打包顺序, 什么是一维度顺序呢?\n\n比如, 我们要打包如下的格式:\n\n````\n{\n a =\u003e [b, c]\n d =\u003e e\n}\n````\n\n那么打包的过程就是:\n\n````c\nyar_packager *pk = yar_pack_start_map( 2); //我们是一个2个kv的MAP\n\nyar_pack_push_string(pk, \"a\", 1); //压入第一个key, a\n\nyar_pack_push_array(pk, 2);  //压入第一个key对应的一个2个元素的array\n\nyar_push_string (pk, \"b\", 1);   //第一个元素\n\nyar_push_string (pk, \"c\", 1);   //第二个元素 此时数组已经填充完毕\n\nyar_push_string(pk, \"d\", 1);   //压入第二个key d\n\nyar_push_string(pk, \"e\", 1);   //压入第二个key对应的e\n````\n\n\n来看一个实际的例子(在example/server.c)\n\n\n这个API返回了一个3个kv的map给客户端, 第一个元素是status值是long 0\n\n第二个元素是parameters, 传回了客户端请求的参数\n\n第三个是一个map, 返回了一些随意的值.\n\n最后调用yar\\_response\\_set\\_retval设置好返回值, 然后释放内存.\n\n### 总结\n\n 最后, Yar的代码中包含了一个Server 和一个Client的例子, 在example目录下.\n\n\n 目前已经实现了PHP端请求C服务, C服务互相请求, 考虑到PHP只能通过HTTP协议提供RPC服务, 所以C请求PHP的, 还暂时没有开放.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaruence%2Fyar-c","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaruence%2Fyar-c","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaruence%2Fyar-c/lists"}