美文网首页Yar Rpc框架源码解读PHP源码分析
[原]PHP-yar拓展源码解读四-client篇

[原]PHP-yar拓展源码解读四-client篇

作者: bromine | 来源:发表于2018-08-26 12:40 被阅读0次

    Yar拓展自带了一个Yar的PHP客户端,这一章先讲yar的同步客户端实现。

    我们假设使用代码如下:

    //demo.php
    $client = new Yar_Client("http://www.demoserver.com/rpc/s.php");
    $client->SetOpt(YAR_OPT_CONNECT_TIMEOUT, 1000);
    echo $client->getOrderMoney(123456);
    

    Client构造器

    从Yar_Client类的构造器看起,Yar_Client的源码实现如下:

    //yar_client.c
    /* {{{ proto Yar_Client::__construct($uri[, array $options = NULL]) */
    PHP_METHOD(yar_client, __construct) {
        zend_string *url;
        zval *options = NULL;
    
        //使用url,option变量来接收构造器的两个参数,第一个为字符串,第二个为可选参数,可空,类型为数组
        if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "S|a!", &url, &options) == FAILURE) {
            return;
        }
    
        zend_update_property_str(yar_client_ce, getThis(), ZEND_STRL("_uri"), url);
        if (strncasecmp(ZSTR_VAL(url), "http://", sizeof("http://") - 1) == 0
                || strncasecmp(ZSTR_VAL(url), "https://", sizeof("https://") - 1) == 0) {
                  //_protocol的默认值是 YAR_CLIENT_PROTOCOL_HTTP,无需处理
        } else if (strncasecmp(ZSTR_VAL(url), "tcp://", sizeof("tcp://") - 1) == 0) {
            zend_update_property_long(yar_client_ce, getThis(), ZEND_STRL("_protocol"), YAR_CLIENT_PROTOCOL_TCP);
        } else if (strncasecmp(ZSTR_VAL(url), "unix://", sizeof("unix://") - 1) == 0) {
            zend_update_property_long(yar_client_ce, getThis(), ZEND_STRL("_protocol"), YAR_CLIENT_PROTOCOL_UNIX);
        } else {
            php_yar_client_trigger_error(1, YAR_ERR_PROTOCOL, "unsupported protocol address %s", ZSTR_VAL(url));
            return;
        }
    
        if (options) {
            zend_update_property(yar_client_ce, getThis(), ZEND_STRL("_options"), options);
        }
    }
    /* }}} */
    

    Yar_Client的构造器就是将uri,options参数,以及从$uri中解析出的通信协议写入成员变量而已。

    万能的__call()

    不像Java系的RpcClient实现,动不动来个动态代理设计,在PHP中一个__call()足以动态"生成"任意远程方法。
    对Yar_Client实例的所有调用,都直接通过__call()魔术方法对远程Yar服务器执行远程调用。

    /* {{{ proto Yar_Client::__call($method, $parameters = NULL) */
    PHP_METHOD(yar_client, __call) {
        zval *params, *protocol, rv;
        zend_string *method;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa", &method, &params) == FAILURE) {
            return;
        }
    
        protocol = zend_read_property(yar_client_ce, getThis(), ZEND_STRL("_protocol"), 0, &rv);
        //Yar拓展目前客户端支持UnixSocket Tcp Http三种协议,根据使用的uri隐式确认,不同的协议会使用不同的传输器
        switch (Z_LVAL_P(protocol)) {
            case YAR_CLIENT_PROTOCOL_TCP:
            case YAR_CLIENT_PROTOCOL_UNIX:
            case YAR_CLIENT_PROTOCOL_HTTP:
                if ((php_yar_client_handle(Z_LVAL_P(protocol), getThis(), method, params, return_value))) {
                    return;
                }
                break;
            default:
                php_error_docref(NULL, E_WARNING, "unsupported protocol %ld", Z_LVAL_P(protocol));
                break;
        }
    
        RETURN_FALSE;
    }
    /* }}} */
    

    Yar_Client->__call()的唯一有效操作是执行php_yar_client_handle()

    static int php_yar_client_handle(int protocol, zval *client, zend_string *method, zval *params, zval *retval) /* {{{ */ {
        char *msg;
        zval *uri, *options;
        zval rv;
        const yar_transport_t *factory;
        yar_transport_interface_t *transport;
        yar_request_t *request;
        yar_response_t *response;
        int flags = 0;
    
        uri = zend_read_property(yar_client_ce, client, ZEND_STRL("_uri"), 0, &rv);
        //根据协议(from uri)获取传输器实例
        if (protocol == YAR_CLIENT_PROTOCOL_HTTP) {
            factory = php_yar_transport_get(ZEND_STRL("curl"));
        } else if (protocol == YAR_CLIENT_PROTOCOL_TCP || protocol == YAR_CLIENT_PROTOCOL_UNIX) {
            factory = php_yar_transport_get(ZEND_STRL("sock"));
        } else {
            return 0;
        }
    
        //传输器和传输器工厂的细节见传输器章节
        transport = factory->init();
     
        options = zend_read_property(yar_client_ce, client, ZEND_STRL("_options"), 1, &rv);
    
        if (IS_ARRAY != Z_TYPE_P(options)) {
            options = NULL;
        }
        //构造request
        if (!(request = php_yar_request_instance(method, params, options))) {
            transport->close(transport);
            factory->destroy(transport);
            return 0;
        }
    
        if (YAR_G(allow_persistent)) {
            if (options) {
                zval *flag = php_yar_client_get_opt(options, YAR_OPT_PERSISTENT);
                if (flag && (Z_TYPE_P(flag) == IS_TRUE || (Z_TYPE_P(flag) == IS_LONG && Z_LVAL_P(flag)))) {
                    flags |= YAR_PROTOCOL_PERSISTENT;
                }
            }
        }
    
        /* This is tricky to pass options in, for custom headers*/
        msg = (char*)options;
        //调用传输器实例的open()接口方法 (具体内容见传输器章节)
        if (!transport->open(transport, Z_STR_P(uri), flags, &msg)) {
            php_yar_client_trigger_error(1, YAR_ERR_TRANSPORT, msg);
            php_yar_request_destroy(request);
            efree(msg);
            transport->close(transport);
            factory->destroy(transport);
            return 0;
        }
    
        DEBUG_C(ZEND_ULONG_FMT": call api '%s' at (%c)'%s' with '%d' parameters",
                request->id, ZSTR_VAL(request->method), (flags & YAR_PROTOCOL_PERSISTENT)? 'p' : 'r', Z_STRVAL_P(uri), 
                zend_hash_num_elements(Z_ARRVAL(request->parameters)));
    
        //调用传输器实例的send()接口方法
        if (!transport->send(transport, request, &msg)) {
            php_yar_client_trigger_error(1, YAR_ERR_TRANSPORT, msg);
            php_yar_request_destroy(request);
            efree(msg);
            transport->close(transport);
            factory->destroy(transport);
            return 0;
        }
    
        //调用传输器实例的exec()接口方法,获取响应对应的response变量
        response = transport->exec(transport, request);
    
        if (response->status != YAR_ERR_OKEY) {
            php_yar_client_handle_error(1, response);
            php_yar_request_destroy(request);
            php_yar_response_destroy(response);
            transport->close(transport);
            factory->destroy(transport);
            return 0;
        } else {
            //打印 远程服务器的(如)标准输出,事实上没有人会糟糕到直接利用远程服务的标准输出
            //这个功能的主要使用场景有两个 
            //1个是代替远程服务器打印各种Notice,Warning信息
            //2是将RPC服务文档从RPC Server的标准输出转移到RPC Client的标准输出上。
            if (response->out && ZSTR_LEN(response->out)) {
                PHPWRITE(ZSTR_VAL(response->out), ZSTR_LEN(response->out));
            }
            //从responce中获取远程RPC方法的返回值作为PHP方法的返回值返回
            ZVAL_COPY(retval, &response->retval);
            //相关资源释放
            php_yar_request_destroy(request);
            php_yar_response_destroy(response);
            transport->close(transport);
            factory->destroy(transport);
            return 1;
        }
    } /* }}} */
    
    

    整理来说还是比较容易理解的,传输细节在传输器章节中再讲。

    相关文章

      网友评论

        本文标题:[原]PHP-yar拓展源码解读四-client篇

        本文链接:https://www.haomeiwen.com/subject/cactvftx.html