phpple / php-dubbo-proxy

A dubbo's proxy for php developer, based on dubbo's telnet protocol.
3 stars 0 forks source link

noResponse #1

Open elrond-g opened 5 years ago

elrond-g commented 5 years ago

老哥,这插件经常报错noresponse,我看了下,是因为返回结果如果带异常,就这样了。经常发生在数据结构比较复杂的场景,以及数据量传输比较大,或者是响应比较慢的场景。然后我用swoole重写了组件,貌似稳定很多。

懒得提交pull request了,直接把proxy.php文件贴这里了。如果有需要讨论的地方,联系我就行了。github的邮箱对应的qq号

`<?php /**

namespace phpple\php_dubbo_proxy;

class Proxy { const DUBBO_NO_SUCH_METHOD_FLAG = 'No such method '; const DUBBO_NO_SUCH_SERVICE_FLAG = 'No such service '; const DUBBO_NORMAL_END_FLAG = 'elapsed: '; const DUBBO_NULL_RESULT_FLAG = "null\r\n"; const DUBBO_HINT_FLAG = 'dubbo>';

/**
 * Zookeeper's instance
 * @var \Zookeeper
 */
private $zoo;
/**
 * Registry's address
 * @var string
 */
private $registry = '127.0.0.1:2181';
/**
 * Provider's version
 * @var string
 */
private $version = "1.0.0";

/**
 * Service's name
 * @var string
 */
private $service;

private function __construct($service, $registry = null, $version = null) {
    $this->service = $service;
    if ($registry !== null) {
        $this->registry = $registry;
    }
    if ($version !== null) {
        $this->version = $version;
    }
}

/**
 * Get the proxy for custom service
 * @param $service service's name
 * @param array $conf configure,such as registry, version
 * @return Proxy
 */
public static function getService($service, $conf = array()) {
    $registry = null;
    if (isset($conf['registry'])) {
        $registry = $conf['registry'];
    }
    $version = null;
    if (isset($conf['version'])) {
        $version = $conf['version'];
    }
    return new self($service, $registry, $version);
}

/**
 * Invoke service's method
 * @param $name method's name
 * @param $arguments arguments
 * @return mixed
 */
public function __call($name, $arguments) {
    $provider = $this->getProvider($this->service, $name);
    if (empty($provider)) {
        throw new \Exception('dubbo.providerNotFound');
    }
    $ret = $this->invoke($provider, $name, $arguments);
    return json_decode($ret, true);
}

/**
 * Get the provider for the custom service and method
 * @param $service
 * @param $method
 * @return bool|array
 */
private function getProvider($service, $method) {
    if (!$this->zoo) {
        $this->zoo = new \Zookeeper($this->registry);
    }
    $path = sprintf('/dubbo/%s/providers', $service);
    $rows = $this->zoo->getChildren($path);
    $providers = [];
    foreach ($rows as $row) {
        $info = parse_url(rawurldecode($row));

        $options = [];
        if (isset($info['query'])) {
            parse_str($info['query'], $options);
        }
        unset($info['query']);
        $info['options'] = $options;

// if ($this->version && $info['options']['version'] != $this->version) { // continue; // } $info['options']['methods'] = explode(',', $info['options']['methods']); if (!in_array($method, $info['options']['methods'])) { continue; }

        $providers[] = $info;
    }
    $num = count($providers);
    if ($num == 0) {
        return false;
    }
    if ($num > 1) {
        $index = mt_rand(0, $num - 1);
        $provider = $providers[$index];
    } else {
        $provider = $providers[0];
    }
    return $provider;
}

/**
 * Call dubbo's remoting method
 * @param $provider
 * @param $method
 * @param $args
 * @return string
 * @throws \Exception
 */
private function invoke($provider, $method, $args){

    $timeout = isset($provider['options']['timeout']) ? $provider['options']['timeout'] / 1000 : 5;
    $args = json_encode($args);
    $args = substr($args, 1, -1);
    $cmd = sprintf("invoke %s.%s(%s)", $provider['options']['interface'], $method, $args);

    $client = new \swoole_client(SWOOLE_SOCK_TCP);
    if (!$client->connect($provider['host'], $provider['port'], -1))
    {
        throw new \RuntimeException("connect failed. Error: {$client->errCode}\n");
    }
    $client->send($cmd . "\n");
    $ret = $client->recv();
    $client->close();
    if(strstr($ret, self::DUBBO_NORMAL_END_FLAG)){
        $resArr= explode(self::DUBBO_NORMAL_END_FLAG, $ret);
        $res = trim($resArr[0]);
        return $res;
    }

    if ($ret === self::DUBBO_NULL_RESULT_FLAG) {
        throw new \RuntimeException("dubbo.nullResult : "+$ret);
    }

    if (strstr($ret, self::DUBBO_NO_SUCH_METHOD_FLAG)) {
        throw new \RuntimeException("dubbo.noMethod : "+$ret);
    }
    if (strstr($ret, self::DUBBO_NO_SUCH_SERVICE_FLAG)) {
        throw new \RuntimeException('dubbo.noService : '+$ret);
    }
    throw new \RuntimeException("dubbo.unknowError:" + $ret);
}
private function invoke2($provider, $method, $args) {
    $fh = fsockopen($provider['host'], $provider['port']);
    $timeout = isset($provider['options']['timeout']) ? $provider['options']['timeout'] / 1000 : 5;
    stream_set_blocking($fh, 0);
    stream_set_write_buffer($fh, 0);
    stream_set_timeout($fh, $timeout);
    $args = json_encode($args);
    $args = substr($args, 1, -1);
    $cmd = sprintf("invoke %s.%s(%s)", $provider['options']['interface'], $method, $args);
    fwrite($fh, $cmd . PHP_EOL);

    $output = [];
    $num = 0;

    while (!feof($fh)) {
        $buffer = fgets($fh);
        if ($buffer === self::DUBBO_NULL_RESULT_FLAG) {
            $output[] = 'null';
            break;
        }
        if (strncmp($buffer, self::DUBBO_NORMAL_END_FLAG, strlen(self::DUBBO_NORMAL_END_FLAG)) === 0) {
            break;
        }
        if (strncmp($buffer, self::DUBBO_NO_SUCH_METHOD_FLAG, strlen(self::DUBBO_NO_SUCH_METHOD_FLAG)) === 0) {
            fclose($fh);
            throw new \Exception("dubbo.noMethod : "+$buffer);
        }
        if (strncmp($buffer, self::DUBBO_NO_SUCH_SERVICE_FLAG, strlen(self::DUBBO_NO_SUCH_SERVICE_FLAG)) === 0) {
            fclose($fh);
            throw new \Exception('dubbo.noService : '+$buffer);
        }
        if ($buffer !== false) {
            $output[] = rtrim($buffer);
        } else {
            $num++;
            if ($num > 100000000) {
                trigger_error('dubbo.noResponse', E_USER_ERROR);
                break;
            }
        }
    }
    fclose($fh);
    $result = implode('', $output);
    if (strncmp($result, self::DUBBO_HINT_FLAG, strlen(self::DUBBO_HINT_FLAG)) === 0) {
        $line = substr($result, strlen(self::DUBBO_HINT_FLAG));
    }
    // if charset is defined in configure, convert is not required
    return $result;
    //这里是转化,但是没提供接口修改字符集,所以直接写死utf-8了
    //      if (isset($provider['options']['charset']) && strtolower($provider['options']['charset']) == 'utf-8') {
    //          return $result;
    //      }
    //      return iconv('gbk', 'utf-8', $result);
}

}`

comdeng commented 5 years ago

多谢!