使用thrift做项目的时候,用到了thrift框架,后来遇到一个很棘手的问题,就是在使用TBinaryProtocol TFramedTransport TNonblockingServerSocket等协议时,服务器端不支持获取client的ip地址。
经过了几天的研究,发现如下方法可以极简(三行)更改代码的同时,解决获取ip的问题。
涉及thrift 协议:TBinaryProtocol TFramedTransport TNonblockingServerSocket
public static void main(String[] args) {
try {
TNonblockingServerSocket socket = new TNonblockingServerSocket(PORT);
TProcessor processor = new KkLog.Processor(new KkServiceImpl());
TThreadedSelectorServer.Args arg = new TThreadedSelectorServer.Args(socket);
arg.transportFactory(new TFramedTransport.Factory());
arg.protocolFactory(new TBinaryProtocol.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new TThreadedSelectorServer(arg);
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
if(args.length<1){
System.out.println("run as java -jar KkClient.jar serverIP");
return ;
}
TTransport transport = new TFramedTransport(new TSocket(IP, PORT, clientTimeout));
TProtocol protocol = new TBinaryProtocol(transport);
KkLog.Client client = new KkLog.Client(protocol);
try {
transport.open();
client.sendLog("0.0.0.0", data); //这里客户端调用sendLog发送data,前面是ip地址,由服务器接收到后填充。
} catch (TApplicationException e) { // 异常的文档 http://people.apache.org/~thejas/thrift-0.9/javadoc/org/apache/thrift/TException.html
System.out.println(e.getMessage() + " " + e.getType());
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
transport.close();
}
从maven下载了thrift-0.9.3版本的源码,修改如下:
package org.apache.thrift.protocol;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
/**
* Binary protocol implementation for thrift.
* Customized by tongange@haizhi.com 20160830
*/
public class TBinaryProtocol extends TProtocol {
private static final TStruct ANONYMOUS_STRUCT = new TStruct();
private static final long NO_LENGTH_LIMIT = -1;
protected static final int VERSION_MASK = 0xffff0000;
protected static final int VERSION_1 = 0x80010000;
public String client_ip="0.0.0.0"; //《《《《《《《《《《—————————在这里增加
/**
* The maximum number of bytes to read from the transport for
* variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
* unlimited.
*/
private final long stringLengthLimit_;
public void invoke() {
frameTrans_.reset(buffer_.array());
response_.reset();
try {
if (eventHandler_ != null) {
eventHandler_.processContext(context_, inTrans_, outTrans_);
}
//下面这行是从TNonblockingSocket中获取socketChannel进而获取client 的ip。并保存到inProt_的新加字段中。下面的强制转换根据自己实际用到的协议实际填写。((TBinaryProtocol)inProt_).client_ip=((InetSocketAddress)((TNonblockingSocket)this.trans_).getSocketChannel().getRemoteAddress()).getAddress().getHostAddress();
processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);
responseReady();
return;
} catch (TException te) {
LOGGER.warn("Exception while invoking!", te);
} catch (Throwable t) {
LOGGER.error("Unexpected throwable while invoking!", t);
}
// This will only be reached when there is a throwable.
state_ = FrameBufferState.AWAITING_CLOSE;
requestSelectInterestChange();
}
private static class sendLog_argsStandardScheme extends StandardScheme<sendLog_args> {
public void read(org.apache.thrift.protocol.TProtocol iprot, sendLog_args struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TField schemeField;
iprot.readStructBegin();
while (true)
{
schemeField = iprot.readFieldBegin();
if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
break;
}
switch (schemeField.id) {
case 1: // IP
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.ip = iprot.readString();
struct.ip = ((TBinaryProtocol)iprot).client_ip; //在这里添加使用我们保存的ip覆盖客户端传来的。
//因为客户端无法获得自己的ip,所以客户端发来什么都无所谓,保留接口在服务器端填充就行。
struct.setTypeIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // LOG
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.log = iprot.readString();
struct.setLogIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
default:
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
}下面修改thrift生成的servive 文件中增加一行代码: public void write(org.apache.thrift.protocol.TProtocol oprot, sendLog_args struct) throws org.apache.thrift.TException {
struct.validate();
oprot.writeStructBegin(STRUCT_DESC);
if (struct.type != null) {
oprot.writeFieldBegin(TYPE_FIELD_DESC);
oprot.writeString(struct.type);
oprot.writeFieldEnd();
}
if (struct.log != null) {
oprot.writeFieldBegin(LOG_FIELD_DESC);
oprot.writeString(struct.log);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
}
至此我们完整实现了使thrift支持获取客户端ip。
上述更改需要首先系在thrift源码包: libthrift-0.9.3-sources.jar。修改代码后再本地编译生成jar包,在使用工程导入即可。
注:一定要注意自己使用的协议是否与文中使用的协议的相同,如果不同需要根据原理进行相应适配。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。