请求的底层结构
//yar_request.h
typedef struct _yar_request {
zend_ulong id;
zend_string *method;
zval parameters;
/* following fileds don't going to packager */
zval options;
} yar_request_t;
Yar底层使用yar_request_t
来描述一个请求的所有信息.
以以下RPC底层调用为例
/** @var Yar_Client $client */
$client->doSth($parm1,$pram2);
- id成员使用
mt_rand()
生成的随机数填充,用于(弱)唯一标识一个(来回)的 rpc请求。 - method 对应着 rpc调用的远程方法,此处为
'doSth'
- parameters 对应着RPC调用方法中的传参,此处的parameters类型为zval 而不是zval[],是因为yar的方法调用基于PHP的魔术方法
__call()
。而这里parameters
对应__call()
的第二个参数变量,即[$parm1,$pram2]
,或者说...$parameters
等同于$parm1,$pram2
- options 对应Yar_Client的配置/
$_options
属性,包含打包方式,超时时间等,
请求的序列化ZVAL
//yar_request.c
zend_string *php_yar_request_pack(yar_request_t *request, char **msg) /* {{{ */ {
zval zreq;
zend_string *payload;
char *packager_name = NULL;
/* @TODO: this is ugly, which needs options stash in request */
if (IS_ARRAY == Z_TYPE(request->options)) {
zval *pzval;
if ((pzval = zend_hash_index_find(Z_ARRVAL(request->options), YAR_OPT_PACKAGER)) && IS_STRING == Z_TYPE_P(pzval)) {
packager_name = Z_STRVAL_P(pzval);
}
}
array_init(&zreq);
add_assoc_long_ex(&zreq, ZEND_STRL("i"), request->id);
add_assoc_str_ex(&zreq, ZEND_STRL("m"), zend_string_copy(request->method));
if (IS_ARRAY == Z_TYPE(request->parameters)) {
Z_TRY_ADDREF(request->parameters);
add_assoc_zval_ex(&zreq, ZEND_STRL("p"), &request->parameters);
} else {
zval tmp;
array_init(&tmp);
add_assoc_zval_ex(&zreq, ZEND_STRL("p"), &tmp);
}
if (!(payload = php_yar_packager_pack(packager_name, &zreq, msg))) {
zval_ptr_dtor(&zreq);
return NULL;
}
zval_ptr_dtor(&zreq);
return payload;
}
为了能够将一个拓展层面/C层面的yar_request_t
能够方便的作为一个zval被打包器打包,yar_request_t
会被先用来生成一个具有三个成员的中转php数组--,其中yar_request_t->option成员被舍弃。
生成的IMP数组
临时变量如下
//IMP数组
array(
'i'=>yar_request_t->id,
'm'=>‘doSth’,
'p'=>array($parm1,$pram2)
)
打包器会对该IMP数组
进行序列化作为payload的正文,具体参考 packager篇和protocol篇。
响应的底层结构
typedef struct _yar_response {
long id;
int status;
zend_string *out;
zval err;
zval retval;
} yar_response_t;
Yar底层使用yar_response_t
来描述一个响应的所有信息.
- id同yar_request_t->id 同一个RPC调用,request和response的id是相同的
- 远程服务器的(原)标准输出
- 错误码,默认的0代表
YAR_ERR_OKEY
- 服务端发生异常时,status为 0x40 ,此时的err是一个PHP数组,发生异常的时候保存着excetion的相关信息,结构如下
array(
'message'=>$exceprtion->$message,
'code'=>$exceprtion->$code,
'file'=>$exceprtion->$file,
'line'=>$exceprtion->$line,
'_type'=>$exceprtion::class,
)
其他报错的情况下,err为字符串,直接存放服务端的错误msg。
- retval 表示远程服务器中rpc方法的返回变量
响应的序列化ZVAL
类似yar_request_t
的IMP数组
,yar_response_t
也有一个对应的中转zval,将isroe数组转换成yar_response_t变量
//ISROE数组
array(
"i" => yar_response_t->id,
"s" =>yar_response_t->status,
"r" => yar_response_t->out,
"o" =>yar_response_t->retval,
"e" => yar_response_t->err,
)
这里可以看下该zval转yar_response_t
的代码
//yar_response.c
void php_yar_response_map_retval(yar_response_t *response, zval *ret) /* {{{ */ {
if (IS_ARRAY != Z_TYPE_P(ret)) {
return;
} else {
zval *pzval;
HashTable *ht = Z_ARRVAL_P(ret);
if ((pzval = zend_hash_str_find(ht, ZEND_STRL("i"))) == NULL) {
return;
}
convert_to_long(pzval);
response->id = Z_LVAL_P(pzval);
if ((pzval = zend_hash_str_find(ht, ZEND_STRL("s"))) == NULL) {
return;
}
convert_to_long(pzval);
if ((response->status = Z_LVAL_P(pzval)) == YAR_ERR_OKEY) {
if ((pzval = zend_hash_str_find(ht, ZEND_STRL("o"))) != NULL) {
response->out = Z_STR_P(pzval);
ZVAL_NULL(pzval);
}
if ((pzval = zend_hash_str_find(ht, ZEND_STRL("r"))) != NULL) {
ZVAL_COPY(&response->retval, pzval);
}
} else if ((pzval = zend_hash_str_find(ht, ZEND_STRL("e"))) != NULL) {
ZVAL_COPY(&response->err, pzval);
}
}
}