背景:
有一个项目做一个系统, 分客户端和服务端, 客户端用 c++ 写的, 用来收集信息然后传给服务端 (客户端的数量还是比较多的, 正常的有几千个),
服务端用 Java 写的 (带管理页面), 属于 RPC 模式, 中间的通信框架使用的是 thrift.
thrift 很多优点就不多说了, 它是 Facebook 的开源的 rpc 框架, 主要是它能够跨语言, 序列化速度快, 但是他有个不讨喜的地方就是它必须用自己 IDL 来定义接口
thrift 版本: 0.9.2.
问题定位与分析
步骤一. 初步分析
客户端无法连接服务端, 查看服务器的端口开启状况, 服务端口并没有开启. 于是启动服务端, 启动几秒后, 服务端崩溃, 重复启动, 服务端依旧在启动几秒后崩溃.
步骤二. 查看服务端日志分析
如果想学习 Java 工程化, 高性能及分布式, 深入浅出. 微服务, Spring,MyBatis,Netty 源码分析的朋友可以加我的 Java 高级交流: 854630135, 群里有阿里大牛直播讲解技术, 以及 Java 大型互联网技术的视频免费分享给大家.
分析得知是因为 java.lang.OutOfMemoryError: Java heap space(堆内存溢出) 导致的服务崩溃.
客户端搜集的主机信息, 主机策略都是放在缓存中, 可能是因为缓存较大造成的, 但是通过日志可以看出是因为 Thrift 服务抛出的堆内存溢出异常与缓存大小无关.
步骤三. 再次分析服务端日志
可以发现每次抛出异常的时候都会伴随着几十个客户端在向服务端发送日志, 往往在发送几十条日志之后, 服务崩溃. 可以假设是不是堆内存设置的太小了?
查看启动参数配置, 最大堆内存为 256MB. 修改启动配置, 启动的时候分配更多的堆内存, 改成 java -server -Xms512m -Xmx768m.
结果是, 能坚持多一点的时间, 依旧会内存溢出服务崩溃. 得出结论, 一味的扩大内存是没有用的.
** 为了证明结论是正确的, 做了这样的实验:**
> 内存设置为 256MB, 在公司服务器上部署了服务端, 使用 Java VisualVM 远程监控服务器堆内存.
> 模拟客户现场, 注册 3000 个客户端, 使用 300 个线程同时发送日志.
> 结果和想象的一样, 没有出现内存溢出的情况, 如下图:
- public boolean read() { // try to read the frame size completely
- if (this.state_ == AbstractNonblockingServer.FrameBufferState.READING_FRAME_SIZE) {
- if (!this.internalRead()) {
- return false;
- }
- // if the frame size has been read completely, then prepare to read the actual time
- if (this.buffer_.remaining() != 0) {
- return true;
- }
- int frameSize = this.buffer_.getInt(0);
- if (frameSize <= 0) {
- this.LOGGER.error("Read an invalid frame size of" + frameSize + ". Are you using TFramedTransport on the client side?");
- return false;
- }
- // if this frame will always be too large for this server, log the error and close the connection.
- if ((long)frameSize> AbstractNonblockingServer.this.MAX_READ_BUFFER_BYTES) {
- this.LOGGER.error("Read a frame size of" + frameSize + ", which is bigger than the maximum allowable buffer size for ALL connections.");
- return false;
- }
- if (AbstractNonblockingServer.this.readBufferBytesAllocated.get() + (long)frameSize> AbstractNonblockingServer.this.MAX_READ_BUFFER_BYTES) {
- return true;
- }
- AbstractNonblockingServer.this.readBufferBytesAllocated.addAndGet((long)(frameSize + 4));
- this.buffer_ = ByteBuffer.allocate(frameSize + 4);
- this.buffer_.putInt(frameSize);
- this.state_ = AbstractNonblockingServer.FrameBufferState.READING_FRAME;
- }
- if (this.state_ == AbstractNonblockingServer.FrameBufferState.READING_FRAME) {
- if (!this.internalRead()) {
- return false;
- } else {
- if (this.buffer_.remaining() == 0) {
- this.selectionKey_.interestOps(0);
- this.state_ = AbstractNonblockingServer.FrameBufferState.READ_FRAME_COMPLETE;
- }
- return true;
- }
- } else {
- this.LOGGER.error("Read was www.jiahuayulpt.com called but state is invalid ("www.thd178.com/ + this.state_ + ")");
- return false;
- }
- }
- public abstract static class AbstractNonblockingServerArgs<T extends AbstractNonblockingServer.AbstractNonblockingServerArgs<T>> extends AbstractServerArgs<T> www.dfgjyl.cn {
- <br>
- public long maxReadBufferBytes =www.120xh.cn 9223372036854775807L;
- public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
- super(transport)www.yongshiyule178.com;
- this.transportFactory(new www.yongshi123.cn Factory());
- }
- }
来源: http://www.bubuko.com/infodetail-2982947.html