李成笔记网

专注域名、站长SEO知识分享与实战技巧

记录一个线上环境的CPU高负载服务不可用的问题排查和解决

问题现象

后台服务三个部署节点偶发cpu 200%占用,服务进程还在,但服务不可用,等待3-10分钟,服务恢复正常。

分析

根据以往项目经验分析,初步怀疑是本身代码问题,大量创建对象,频繁触发full gc。

排查

1、查看gc情况

通过gcutil可以看到服务gc情况正常。

2、查看tcp监控,TCP状态正常,可以排除是http请求第三方超时带来的问题。

3.查看线程栈

1)jps 获取服务进程PID

2)查看进程下的线程情况

3)找到cpu占用率高的线程PID(写文档时服务已恢复正常,截图非出现问题时截图),当时现象为两个线程cpu占用率99%。查看这两个线程栈信息。将线程ID按16进制展示,执行指令“printf "%x\n" PID”。

4)执行指令“jstack 进程PID | grep "16进制线程号" -C5 --color”:

修复

通过查看线程栈第4步,可以看到两个cpu占用搞的线程分别对应的是gc线程和一个业务线程号。

方向1:既然有gc线程,说明gc还是有问题,此时在查看服务的gc状况多了一次full gc,调整内存,加大内存到32G,避免full gc, 修改java8默认垃圾回收器,CMS,ParNew 换成G1。结论:问题未解决。

方向2:另一个业务线程号, 根据业务线程号 grep 历史日志(后台线程号做了重命名,每个线程号都是唯一),此时可以根据线程号定位到对应的接口:url转base64,日志中提取用户的请求报文,再次调用url转base64接口,果然,问题复现,再次犯症,服务不可用,cpu飚到200%。重启服务 开启arthas工具,定位到 set值以后该线程就一直卡顿不在返回,至此确定是业务代码问题。查看日志打印步骤,初步怀疑是统一返回fastJson 序列化问题。

旧代码如下(关键逻辑代码):

    @GetMapping("/urlToBase64")
    public UrltoBease64Vo imgUrlToBase64(@RequestParam("imageUrl") String imageUrl){
        UrltoBease64Vo vo = new UrltoBease64Vo();
        String base64 = urlToBase64(imageUrl);
        vo.setBase64(base64);
        return vo;
    }

整改方向:该服务配置了统一返回时序列化为fastJson,不再走统一返回绕过统一返回时的序列化,即绕过fastjson序列化。

新代码如下(只列出修改部分):

    public void imgUrlToBase64(HttpServletResponse response, @RequestParam("imageUrl") String imageUrl){
        UrltoBease64Vo vo = new UrltoBease64Vo();
        String base64 = urlToBase64(imageUrl);
        vo.setBase64(base64);
        StringBuffer stringBuffer = new StringBuffer(  20480);
        stringBuffer.append("{\"OptCode\":0,\"Data\":{\"Base64\":\"").append(base64).append("\"}}");
        try {
            response.getWriter().write(stringBuffer.toString());
            response.getWriter().flush();
            response.getWriter().close();
        }catch (IOException e) {
            //....
        }
    }

再次尝试有问题的报文请求接口,接口正常返回,至此问题解决。

总结

fastJson对大对象序列化存在问题。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言