weibocom / motan

A cross-language remote procedure call(RPC) framework for rapid development of high performance distributed services.
Other
5.88k stars 1.78k forks source link

Yar协议中文序列化问题 #598

Open JerryChaox opened 6 years ago

JerryChaox commented 6 years ago

yar协议碰到中文之后,在php端出现unpack error

idevz commented 6 years ago

你能给个最小化的复现例子吗?比如你的服务配置,调用的数据什么的?

JerryChaox commented 6 years ago

$client = new Yar_Client("http://localhost:8003/api/merchant"); $result = $client->getMerchant(1);

@yar(“api/merchant”) class MerchantService { public Merchant getMerchant(int MerchantId); }

暴露 demoYar:8003 public Merchant getMerchant(int merchantId) { 数据库查询 }

class Merchant { int id; String name; //有中文的时候出现unpack error }

JerryChaox commented 6 years ago
  1. 没有中文时是可以正常解包的。
  2. 另外yarclient 传过去中文也没出现问题,正常插入数据库。 问题出在从数据库取数据时,php端出现unpack error

数据示例 id:1 name:”阿斯顿”

  1. 我打印了一下序列化时的stringbuffer 阿斯顿这个字符 长度写的是 s:3
JerryChaox commented 6 years ago

a:3:{s:1:"r";O:8:"Merchant":11:{s:2:"id";i:1;s:4:"name";s:3:"asd";s:20:"authenticationNumber";N;s:4:"rate";d:0.2;s:7:"address";s:3:"阿斯顿";s:5:"phone";s:11:"18588557892";s:20:"authenticationStatus";i:1;s:10:"createTime";N;s:10:"updateTime";N;s:6:"chanel";N;s:8:"storeSet";a:0:{}}s:1:"s";s:1:"0";s:1:"i";i:0;}

JerryChaox commented 6 years ago

把中文的字符的长度扩大1倍后,仍然会出现unpack error

JerryChaox commented 6 years ago

PHPUnserializer unserializer = new PHPUnserializer(new String(data, 0, data.length, "ISO-8859-1")); PherializePackager中decode方法解码后的数据包字符串 a:3:{s:1:"i";i:731930421;s:1:"m";s:11:"addMerchant";s:1:"p";a:1:{i:0;a:5:{s:4:"name";s:9:"阿斯顿";s:4:"rate";s:3:"0.2";s:7:"address";s:33:"北京市辖区东城区阿斯顿";s:5:"phone";s:11:"18588557892";s:20:"authenticationStatus";i:1;}}}

name的值为 阿斯顿 address的值为 北京市市辖区东城区

解包和压包时字符串的处理明显不一样

JerryChaox commented 6 years ago

修改代码为下可解决中文问题 Serializer.java

private void serializeString(final String string, final StringBuffer buffer)
    {
        String tempString = string;
        try {
            tempString = new String(string.getBytes("UTF-8"),"ISO-8859-1");
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
        int length = tempString.length(); 
        buffer.append("s:");
        buffer.append(length);
        buffer.append(":\"");
        buffer.append(tempString);
        buffer.append("\";");
    }

PherializePackager.java

public <E> byte[] encode(E value) throws IOException {
        return Pherialize.serialize(value).getBytes("ISO-8859-1");
}
rayzhang0603 commented 6 years ago

非常感谢反馈,PHPserializer是使用的第三方jar包,我们尽快评估一下如何修复这个问题。 推荐使用json或msgpack方式进行序列化

JerryChaox commented 6 years ago

请问yar协议下如何指定序列化方式

rayzhang0603 commented 6 years ago

yar协议需要client调用端指定序列化方式,server端自适应。

php侧可以通过yarclient配置

$client = new Yar_Client("http://10.236.62.73:8003/openapi/yarserver/test");
#$client->SetOpt(YAR_OPT_PACKAGER, "JSON");
#$client->SetOpt(YAR_OPT_PACKAGER, "PHP");
$client->SetOpt(YAR_OPT_PACKAGER, "MSGPACK");
$client->SetOpt(YAR_OPT_CONNECT_TIMEOUT, 2000);
#$client->SetOpt(YAR_OPT_TIMEOUT, 2000);

如果是java client,如下设置:

HttpYarClient yarClient = new HttpYarClient();
    yarClient.setPackageName("JSON");
   // yarClient.setPackageName("MSGPACK");
JerryChaox commented 6 years ago

感谢,请问服务端序列化的方式目前只有一种吗

rayzhang0603 commented 6 years ago

yar协议支持三种序列化方式,PHP、JSON、MSGPACk,server端都已经可以支持。server端根据client发送的协议中的对应字段自动选择对应的序列化方式。 不过目前PHP的序列化并没有比较理想的第三方java扩展包,微博内部使用的是JSON和MSGPACK这两种方式。

idevz commented 6 years ago

@JerryChaox 推荐使用 Motan 最新的跨语言解决方案 Motan2 + Simple 序列化