问题背景
需要根据id通过rpc调用查询具体信息,因为没有提供批量查询的接口,所以做法是挨个遍历查询,那意味着:
如果有100个id,就需要顺序进行100次rpc调用,假设每次rpc接口的调用时间是50ms(这个速度很快了),那单单rpc调用就要占用5s,所以接口的响应会非常慢。下面进行优化。

优化方案:
方案一:让服务方提供批量查询接口,需要服务提供方配合,这里暂不采用。
方案二:rpc服务的调用由顺序调用修改为并行调用,采用线程池实现rpc的并发调用。

具体实现如下:
1)创建线程的类
public class MyThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r);
    }
}

2)创建线程处理类,因为需要获取rpc调用的结果所以是实现callable类
说明:这里需要获取spring提供的bean,获取方式是通过参数传入
public class RegisterTask implements Callable {

private String  registerId;
    private String  cityCode;
    // 这里需要获取spring提供的bean,获取方式是通过参数传入
    private RegisterClient registerClient;
    public RegisterTask(String registerId, RegisterClient registerClient, String  cityCode){
        this.registerId = registerId;
        this.cityCode = cityCode;
        this.registerClient = registerClient;
    }

@Override
    public List<StudentLessonDto> call() throws Exception {
        // 这里就是进行rpc调用
        return registerClient.queryLessonsByRegistId(registerId, cityCode);
    }
}

3)方法getDetail()内部创建线程池
        ThreadFactory namedThreadFactory = new MyThreadFactory();
        int queueCapacity = 10000, corePoolSize = 10, maximumPoolSize = 10;
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 10, TimeUnit.SECONDS, arrayBlockingQueue, namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

4) 方法getDetail()内部线程池提交线程,并获取执行的结果
        // 线程池进行rpc调用
        ArrayList<Future<List<StudentLessonDto>>> futures = new ArrayList<>();
        for (String id : registerIdList) {
            Future future = threadPoolExecutor.submit(new RegisterTask(id, registerClient, cityCode));
            futures.add(future);
        }

// 获取线程调用的结果
        for (Future future : futures) {
            try {
                List<StudentLessonDto> list = (List<StudentLessonDto>) future.get();
                if (!CollectionUtils.isEmpty(list)) {
                    result.put(list.get(0).getRegistId(), list);
                }
            } catch (InterruptedException e) {
                log.error("并发获取报名的讲次异常", e);
            } catch (ExecutionException e) {
                log.error("并发获取报名的讲次异常", e);
            }
        }

之后经过测试和上线。

结果:
第一天没啥问题,第二天开始有问题,主要现象如下:
1) 服务调用异常,异常信息大致如下:
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:503)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(
    .......
Caused by: java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
    at java.base/java.lang.Thread.start0(Native Method)

2)机器无法登陆,异常如下:
root:fork failed: Cannot allocate memory

内存耗用的一干二净,root都没有内存了。最后重启机器才行。

下面定位问题:
最近新上的代码,肯定是我的新增的多线程部分除了内存,耗费了大量内存,但是怎么解释呢??
说明肯定是线程资源没有得到释放,但是我是在方法内部创建的线程池,方法执行后按道理对应的线程池和线程资源应该会释放的(我个人错误的理解)。

但是看到一篇博客(https://blog.csdn.net/lbh199466/article/details/104934207/),
因为核心线程一直没有释放,所以对应的线程池和线程资源并没有释放。

栈中的引用对象就是一种GcRoots, 所以如果核心线程一直不被回收,那么对应的线程与对象资源都不会被回收。线程栈和线程中的对象占用的对象都不会释放。引用关系:ThreadPoolExecutor->Worker->thread,然后因为thread一直不释放,所以对应的worker和池资源也都会不会释放。
所以:
方法内部定义线程池,核心线程数不为零,核心线程不会被回收,导致相关内存资源都不会被释放。
也可以参考:https://zhuanlan.zhihu.com/p/72515308

查看java进程中的线程的个数
验证上面的结论,参考https://blog.csdn.net/blueheart20/article/details/78905267(获取当前进程数的方法)
获取当前服务器上java进程的线程个数。下面挺一种实例
jps 获取java进程pid
top -Hp pid 获取进程中的线程个数 如 Threds:1190 total,代表进程中有1190个线程,可以的确看到当前线程数很多

最终解决方案:
1)参考 https://www.cnblogs.com/qxynotebook/p/7398882.html
在线程池中,有核心线程,对于核心线程超时也回收,所以,需要确保核心线程超时之后也被回收。
解决办法:在结果返回之前设置核心线程也回收:
threadPoolExecutor.allowCoreThreadTimeOut(true);
2)参考:https://blog.csdn.net/wchgogo/article/details/78185643(unable to create new native thread)
栈对内存的消耗:目前没有像堆那样指定最大占用内存,设置Xss指点单个线程的占用内存大小,默认线程占用空间时1M,可以设置为512k。

服务器中影响最多线程数的因素:
1)内存,线程肯定是占用内存的,如果内存耗尽,那自然不能继续创建线程。
单个线程占用内存大小可通过-Xss设置,现在默认1M,一般建议512k就够了。
如果Xss设置过大,则浪费内存空间;
如果Xss设置过小,代码中有遍历或递归导致调用太深的时候,就有可能耗尽StackSpace,爆出StackOverflow的错误;

2)机器设置的最大线程数
操作系统会限制进程允许创建的线程数,使用ulimit -u命令查看限制。某些服务器上此阈值设置的过小,比如1024。

rpc接口并发调用实例相关推荐

  1. webservice restful类型接口的调用实例

    2019独角兽企业重金招聘Python工程师标准>>> 因为项目是采用maven搭建的,所以先把所需要的jar包定义进来,我是采用apache的httpComponents第三方技术 ...

  2. golang rpc单参数调用实例

    rpc服务端server.go 注册服务对外提供服务 package mainimport ("fmt""math""net""n ...

  3. 基于RPC接口的业务侧流量回放

    背 景 在产品需求迭代过程中,功能测试与回归测试是必不可少的两个环节.对于改动较大的项目,首先,确保功能的实现符合产品逻辑并做到100%没有问题离不开有效的功能测试:其次,项目中很多逻辑的改动都是在原 ...

  4. python rpc_对python调用RPC接口的实例详解

    要调用RPC接口,python提供了一个框架grpc,这是google开源的 rpc相关文档: 需要安装的python包如下: 1.grpc安装 pip install grpcio 2.grpc的p ...

  5. android app通过Geth RPC接口实现远程调用

    记录一下APP怎么通过以太坊的RPC接口实现远程调用.此过程的环境为Window7和Android studio2.2.3.以web3_clientVersion为例.更多用法参考Geth JSON- ...

  6. php调用接口搜索的网页源代码,PHP用户管理中常用接口调用实例及解析(含源码)...

    掌握用户的第一步就是将已经关注的粉丝信息保存起来,这个时候就用到获取用户列表接口.公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的Open ...

  7. Aria2 RPC接口协议和Java的本地调用实现

    如果你还没有启动aria2 : 安装和配置 目录 Aria2 RPC接口协议 Java实现 依赖 核心类 调用 Aria2 RPC接口协议 官方文档 方法列表 下载参数 本文中我们使用默认的本地调用, ...

  8. python使用GRPC远程调用rpc接口

    1.需要下载的包 pip install grpcio pip install grpcio-tools pip install protobuf 为了更好的书写proto文件,你可以在pycharm ...

  9. java调用python的RPC接口

    依赖 <!--XMRPC相关依赖--><dependency><groupId>org.apache.xmlrpc</groupId><artif ...

最新文章

  1. Starry Night [USACO]
  2. 第十八篇:java操作Excel要处理和分辨的几个概念
  3. MXNet设计和实现简介
  4. VS Code 中有哪些好用的 Azure 插件?
  5. strcpy用于调用的参数太少_C和汇编如何互相调用?嵌入式工程师必须掌握
  6. cocos 制作动态生成内容的列表_零代码工具,让你在线轻松制作交互内容!
  7. android 各个版本代表图标,使用不同的Android SDK版本的不同图标
  8. 将Excel中的数据导入到MySql数据库中
  9. 【Flink】JobException: Recovery is suppressed by NoRestartBackoffTimeStrategy
  10. webflow ajax,java开发之spring webflow实现上传单个文件及多个文件功能实例
  11. Markdown中如何加入上标、下标?
  12. Linux音频驱动-WAV文件格式分析
  13. Thinkphp金融超市贷超平台源码带三级分销
  14. 【读书笔记】天生不聪明
  15. 举头望明月,低头敲代码
  16. 贝壳找房二手房信息爬虫
  17. Office 365平台及其价值主张
  18. 通信核心网linux,基于linux的双模智能手机实现方案
  19. Ubuntu安装开源终端工具Tabby
  20. 电磁场与仿真软件(16)

热门文章

  1. 创建 Linux 目录结构
  2. 快速学习-去中心化应用与以太坊应用
  3. 数据洞察疫情背后的故事
  4. Permutation Feature Importance
  5. 工具使用-Editor.md编辑器
  6. ECMAScript基础知识总结
  7. UnityWebRequest
  8. 华为平板m6 鸿蒙,第一眼就爱上它:华为平板M6开箱体验
  9. matlab读取mit bih,将MIT-BIH心律失常ECG数据库加载到MATLAB上
  10. K-Means欠采样处理不平衡样本python实现