• JVM实战调优(空格引发的服务异常)


    JVM实战调优

    问题描述

    某一个项目中有一个文字转语音的服务,使用的是科大讯飞的语音转换服务,需要调用三方服务。因其转换服务是一个耗时操作,官方给的demo使用的是 WebSocket 进行数据转换操作。项目中使用线程池进行调用。同时科大讯飞的语音合成有长度限制,官方给出的在[8000字节,约2000个汉字],所以需要分段合成。

    在某一天,客户反应语音无法播放,经过查看服务日志排查,是因为购买的服务到期了,客户重新购买了其余的服务,参数发生了改变,以前的参数无法使用。更改了参数之后,更新部署之后,服务恢复正常。又过了几天,客户反应语音又无法播放了,经过查看日志,发现大部分是成功的,部分发生了失败。

    调优过程

    查看日志的时候,发现部分失败的原因是connection fail,通过 jps 找到对应的应用程序进程号。

    然后通过 top -Hp PID 查看了一个CPU和内存的占用率,都是正常的占用。

    因其使用了线程池,怀疑是线程池占满了,用 就 stack pid 查看线程池之后,发现满了。

    "OkHttp ConnectionPool" #48 daemon prio=5 os_prio=0 tid=0x00007f8d90002800 nid=0x2a7a in Object.wait() [0x00007f8dea5a5000]
       java.lang.Thread.State: TIMED_WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	at java.lang.Object.wait(Object.java:460)
    	at okhttp3.internal.Util.waitMillis(Util.kt:536)
    	at okhttp3.internal.Util.lockAndWaitNanos(Util.kt:522)
    	- locked <0x000000076e9394f8> (a okhttp3.internal.connection.RealConnectionPool)
    	at okhttp3.internal.connection.RealConnectionPool$cleanupRunnable$1.run(RealConnectionPool.kt:49)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    
    "pool-1-thread-3" #46 prio=5 os_prio=0 tid=0x00007f8dd4013000 nid=0x2a78 waiting on condition [0x00007f8deaaa8000]
       java.lang.Thread.State: WAITING (parking)
    	at sun.misc.Unsafe.park(Native Method)
    	- parking to wait for  <0x000000076ea3d738> (a java.util.concurrent.CountDownLatch$Sync)
    	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
    	at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)
    	at com.baiying.trstts.factory.XfTts.getPcm(XfTts.java:206)
    	at com.baiying.trstts.factory.XfTts.text2speechV3(XfTts.java:236)
    	at com.baiying.trstts.controller.TextToSpeechControllerV3.lambda$text2Speech$0(TextToSpeechControllerV3.java:56)
    	at com.baiying.trstts.controller.TextToSpeechControllerV3$$Lambda$426/909090985.run(Unknown Source)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    

    通过异常信息,可以看到是 CountDownLatch 阻塞了整个线程,导致其无法结束,通过走查代码发现,程序中只是在正常结束的时候会 countDown() 唤醒线程,继续向下执行,在失败的时候并不会唤醒线程,遂在程序中增加了在失败的时候也会进行 countDown() 唤醒线程,程序重新部署之后,程序正常。

    为了防止出现问题,这次对程序的日志进行了监控,将失败的文章进行了重新转换,虽然线程可以正常结束,但是转换仍然会出现失败,经过对文字进行严格的分析和多次转换测试,有一小段文字被截取了出来,观察了一会没有发现异常,通过十六进制软件对其进行查看,才发现两个字符有区别,两个字符都是空格,但前一个空格是半角输入法下的空格十六进制是20,全角输入法下的空格是 E38080,然后修改程序,将全角输入法下的空格剔除,再次进行转换,服务正常。

  • 相关阅读:
    MySQL "show users"
    MySQL
    A MySQL 'create table' syntax example
    MySQL backup
    MySQL show status
    Tomcat, pathinfo, and servlets
    Servlet forward example
    Servlet redirect example
    Java servlet example
    How to forward from one JSP to another JSP
  • 原文地址:https://www.cnblogs.com/ice-image/p/14522517.html
Copyright © 2020-2023  润新知