解决Ice 对象大数据量传输Bug

这两天,上司给了一个之前版本的Bug,说是在现场发现的,让尽快解决。由于公司项目管理上并不规范,Bug描述信息很少,主要给了简短的文字描述,和bug发生时界面截图,连复现bug的步骤都没有,最初只是当作性能bug对待的。以下是复现、解决bug的过程。
    通过分析,主要是认为ICE对象之间传输的数据量过大导致的问题,因此,搭建了环境,并制造了大数据量传输的情形,问题复现还算顺利。但调试、修复bug的过程确着实费了番劲。

1. 系统简要描述
先描述下系统大致情况吧,如下图所示
   

+-------------+
|      PHP    |
+-------------+
     | |
+-------------+
| ICE APP A   |
+-------------+
     | |
+-------------+       +--------------+
| ICE APP B   | ———|    DataBase   |
+-------------+       +--------------+


    PHP 服务器程序调用ICE 应用A,有些业务A需要调用B,B访问数据库获取到业务数据并通过 ICE 运行时发送给A;当然PHP与A之间也是通过ICE通信,IcePHP,系统是不是挺简单?

2. GDB 调试
   在Bug复现之后,就开始了GDB调试,很快发现,在B从数据库取回20000条记录后,返回 A 时没了响应;
于是,接下来在 A 调用B 的接口的前后插入了调试代码,重新进行GDB调试,发现代码没有从 B 返回,根据经验,要么发生异常,要么系统崩溃(很容易排除此可能)。这时去研究了下ICE的API,又发现了一个问题,A调用B时是可能抛出异常的,而写代码的人却没有去捕获。我想,接下来我要做的工作,任何做这技术的人都清楚了,那就是去捕获这个异常,查看异常信息。最终,发现是 MemoryLimitException。
    通过查看 ICE Manual 知道,是因为传递的数据量超过了 Ice.MessageSizeMax 值,只需把该属性值调大就可以了;不过很遗憾,我改过之后没起作用,我在想为什么没起作用?于是,在代码中又插入了些调试代码,取得 A、B 的 Ice.MessageSizeMax 的属性值,很奇怪的发现 B 的该属性值为0(即使用默认值,而不是真的为0), 也就是说在配置文件中的配置没起作用了。
    我想起在 Ice 文档中曾看到过, 一个 ICE 应用可以有多个通信器,每个通信器都是独立的,各有各的配置(当然可能相同,完全取决于开发者);同时,B 的通信器器就是A动态生成的,于是猜想可能跟这有关系,即不同的通信器配置不同,其中使用的就是用默认配置的那个通信器。
    于是我查看了 A 生成 B 通信器的代码,果然发现 A没有给 initialize 函数传递配置数据,这导致A所使用的所有B的通信器都是缺省配置,也就是说在配置文件中对 B 的配置都没有任何作用了。
    不过,考虑到 PHP 与 A 之间也是 ice 通信,肯定也存在这个问题。

3. Bug解决
    在定位到问题以后,解决方案也就出炉了:
   (1) 修改 A、B的配置文件,修改(添加) Ice.MessageSizeMax 属性值,使他为一个合适的值
   (2) 修改 A 的代码,读取 B 的配置数据,用作分配 B 通信器的配置
   (3) 修改 PHP 的 ICE配置,设置Ice.MessageSizeMax 属性值为一个合适的值
   

修改之后,进行测试,问题解决。


另一篇解释:

场景:使用ICE做中间件,C++做服务端,PHP做客户端。

由于某个接口的传输数据过大,接口异常:Ice_MemoryLimitException搜索了下是由于Ice的默认传输大小是1MB,最大可以设置为2G。

很明显我们传输的数据超过了1M。找到问题那就好办了,设置下Ice.MessageMaxSize即可,找到PHP.ini加上这面这句话:

ice.options="--Ice.MessageSizeMax=2048"

重启php-fpm,在phpinfo中查看确实是生效了。本以为这样就解决了。

但是刷新页面,异常依旧,Google了下,发现C#或者python都可以在代码中设置MessageSizeMax参数。想到PHP应该也可以直接在代码中设置。

在官方网站找到以下例子:

<?php
require 'Ice.php';
require 'Hello.php';
$communicator = null;
try {
    $data = new Ice_InitializationData;
    $data->properties = Ice_createProperties();
    //记载配置文件
    $data->properties->load("props.cfg");
    $communicator = Ice_initialize($data);
    $proxy = $communicator->stringToProxy("...");
    $hello = Demo_HelloPrxHelper::checkedCast($proxy);
    $hello->sayHello();
} catch(Ice_LocalException $ex) {
    // Deal with exception...
}
if($communicator) {
    try {
        $communicator->destroy();
    } catch(Ice_LocalException $ex) {
        // Ignore.
    }
}
?>

确实是可以设置参数,但是这里是配置的文件的,为了方便我直接使用以下代码进行设置:

$data = new Ice_InitializationData;

$data->properties = Ice_createProperties();
//设置传输数据大小, 单位KB
$data->properties->setProperty('Ice.MessageSizeMax', 2048);


$init = Ice_initialize($data);

OK,刷新之后问题解决。

PS:注意Ice.MessageSizeMax参数需要服务端和客户端同时设置。

参考资料:

C#的设置方式:http://stackoverflow.com/questions/8434429/why-do-i-get-icememorylimitexception-even-with-ice-messagesizemax-2000000

官方提供的调用实例:http://doc.zeroc.com/display/Ice/Application+Notes+for+PHP

Ice.MessageMaxSize的设置BUG:http://blog.chinaunix.net/uid-24439730-id-144103.html






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值