大家好:

我是烤鸭。今年年初的时候,项目接入易盾sdk之后,随着接口调用次数增多(用到易盾sdk的接口),项目整体性能变差。写篇文章做个复盘记录,其实同事已经写过了,我借鉴部分再拓展一些。

问题描述

突然收到服务服务报警,整体服务性能下降。

问题排查

机器cpu有所上升,QPS、GC和内存均正常。人的压力也上来了=.=

CAT查看time_waiting线程数持续上升。

执行jstack 查看线程堆栈

jstack -l pid > 1.txt

发现大量的time_waiting线程,其中90%的线程名字都是这个 idle-connection-evictor

可以看出这个线程来自 hc.client5 ,再找下易盾sdk和这个类的关系吧。

我们看下 AntispamRequester这个类,是易盾请求的一个实例化基类。

可以看到这个类里用到的 ClientProfile 是初始化的HttpClientConfig,并且创建 AntispamClient 对象的时候做了单例判断,看来是不想创建太多这个对象。

public class AntispamRequester {private ClientProfile clientProfile;private ConcurrentHashMap<String, Object> clientMap = new ConcurrentHashMap<>();public AntispamRequester(String secretId, String secretKey) {AssertUtils.notBlank(secretId, "secretId can not be null or empty");AssertUtils.notBlank(secretKey, "secretKey can not be null or empty");this.clientProfile = createDefaultProfile(secretId, secretKey);}//...public static ClientProfile createDefaultProfile(String secretId, String secretKey) {ClientProfile clientProfile = ClientProfile.defaultProfile(new Credentials(secretId, secretKey));HttpClientConfig clientConfig = new HttpClientConfig();clientConfig.setMaxConnectionCountPerRoute(100);clientProfile.setHttpClientConfig(clientConfig);return clientProfile;}//...private <T extends AntispamClient> T createIfAbsent(Class<T> clazz) {String name = clazz.getName();Object client = clientMap.get(name);if (client != null) {return (T) client;}return (T) clientMap.computeIfAbsent(name, k -> {try {return clazz.getDeclaredConstructor(ClientProfile.class).newInstance(clientProfile);} catch (Exception e) {throw new RuntimeException(e);}});}
}

再看下 AntispamClient 这个类:

public abstract class AntispamClient {protected DefaultClient client;public AntispamClient(ClientProfile clientProfile) {// 初始化clientclient = new DefaultClient(clientProfile);//...}

再往下看 HttpClientFactory 的 client的初始化方法,从这得出的结论是易盾封装的hc.client5

public class HttpClientFactory {public static CloseableHttpClient create(HttpClientConfig config) {// ... 无关的先注释return HttpClients.custom().evictIdleConnections(TimeValue.of(config.maxIdleTimeMillis(), TimeUnit.MILLISECONDS)).evictExpiredConnections().setConnectionManager(connManager).setDefaultRequestConfig(requestConfig).useSystemProperties().build();}
}

SDK使用

猜测是引入了新的易盾sdk导致的,因为其他没那么改动,而且是在随着接口调用次数增多(用到易盾sdk的接口),项目整体性能变差。不过易盾的包和线程池等待有什么关系呢。

易盾给的官方demo的写法:

https://github.com/yidun/yidun-java-sdk/blob/b92c803c8c2c8f8d55db27ce3284bb1b6eb97c1f/yidun-java-sdk-demo/src/main/java/com/netease/yidun/sdk/antispam/AbstractDemo.java

package com.netease.yidun.sdk.antispam;import com.netease.yidun.sdk.core.client.ClientProfile;
import com.netease.yidun.sdk.core.endpoint.failover.FixedWindowBreakStrategy;
import com.netease.yidun.sdk.core.http.HttpClientConfig;public class AbstractDemo {protected static AntispamRequester createAntispamRequester(String secretId, String secretKey){// 实例化一个requester,入参需要传入易盾内容安全分配的secretId,secretKeyAntispamRequester antispamRequester = new AntispamRequester(secretId, secretKey);// 可选自定义请求器的参数,如果不需要自定义设置,可跳过,否则请参考如下注释内容:
//        ClientProfile clientProfile = AntispamRequester.createDefaultProfile("SecretId", "SecretKey");
//        // 设置http请求的相关配置
//        HttpClientConfig httpClientConfig = clientProfile.getHttpClientConfig();
//        httpClientConfig.socketTimeoutMillis(60000);
//
//        // 设置固定窗口的熔断配置
//        FixedWindowBreakStrategy.Config breakerConfig = clientProfile.getBreakerConfig();
//        breakerConfig.statWindowMillis(300000);
//
//        // 设置请求失败时的重试次数
//        clientProfile.setMaxRetryCount(2);
//        AntispamRequester antispamRequester = new AntispamRequester(clientProfile);return antispamRequester;}
}

项目里也是按照这个写法的,上面看源码 antispamRequester 里可以封装很多个client对象,而每个client对象相当于对http5封装,并且进行了单例判断,理论上不会出问题。

但是按照官方的demo,如果每次都 new AntispamRequester() 呢。

源码分析

回到最开始的地方,idle-connection-evictor 在哪用到的。是构建 HttpClient 的时候根据 evictExpiredConnections 或者 evictIdleConnections,判断是否开启当前线程。

public CloseableHttpClient build() {// ... if (!this.connManagerShared) {if (closeablesCopy == null) {closeablesCopy = new ArrayList<>(1);}if (evictExpiredConnections || evictIdleConnections) {if (connManagerCopy instanceof ConnPoolControl) {final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl<?>) connManagerCopy,maxIdleTime, maxIdleTime);closeablesCopy.add(new Closeable() {@Overridepublic void close() throws IOException {connectionEvictor.shutdown();try {connectionEvictor.awaitTermination(Timeout.ofSeconds(1));} catch (final InterruptedException interrupted) {Thread.currentThread().interrupt();}}});connectionEvictor.start();}}closeablesCopy.add(connManagerCopy);}return new InternalHttpClient(...);
}

IdleConnectionEvictor 初始化:

这个线程就是个死循环,用来关闭超过最大超时时间的线程的,可以理解为一个清扫线程。

public IdleConnectionEvictor(final ConnPoolControl<?> connectionManager, final ThreadFactory threadFactory,final TimeValue sleepTime, final TimeValue maxIdleTime) {Args.notNull(connectionManager, "Connection manager");this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory("idle-connection-evictor", true);final TimeValue localSleepTime = sleepTime != null ? sleepTime : TimeValue.ofSeconds(5);this.thread = this.threadFactory.newThread(new Runnable() {@Overridepublic void run() {try {while (!Thread.currentThread().isInterrupted()) {localSleepTime.sleep();connectionManager.closeExpired();if (maxIdleTime != null) {connectionManager.closeIdle(maxIdleTime);}}} catch (final InterruptedException ex) {Thread.currentThread().interrupt();} catch (final Exception ex) {}}});
}

回到上面的问题,每new一次,就会多x个死循环线程(x取决于client个数)。

解决方案

如果使用易盾的sdk的话,只要保证 AntispamRequester 是单例的就行,如果使用spring,可以注入到ioc。

 /*** 易盾AntispamRequester对象*/@Bean("yiDunRequester")public AntispamRequester yiDunRequester(){//1.默认方式AntispamRequester antispamRequester = new AntispamRequester(yiDunUrlConfig.getSecretId(), yiDunUrlConfig.getSecretKey());return antispamRequester;}

如果使用http的sdk,无论是 http4还是http5 ,都需要考虑资源关闭。

  1. 不要把下面那两个设为true。 evictIdleConnections 和 evictExpiredConnections (这俩默认是false) 和 evictIdleConnections(设置这个值会把evictIdleConnections 变成true),设置的话会启动清扫线程。

    这时候再看易盾的 HttpClientFactory 这个类,如果不设置这俩参数 evictIdleConnections 和 evictExpiredConnections,其实也没事。但是你偷偷设置完了不通知,就有点说不过去了。

    public class HttpClientFactory {public static CloseableHttpClient create(HttpClientConfig config) {// ... 无关的先注释return HttpClients.custom().evictIdleConnections(TimeValue.of(config.maxIdleTimeMillis(), TimeUnit.MILLISECONDS)).evictExpiredConnections().setConnectionManager(connManager).setDefaultRequestConfig(requestConfig).useSystemProperties().build();}
    }
    
  2. 创建共享对象,不再持续创建HttpClient

        /*** 类实例对象,避免重复创建*/private static HttpClient httpClient = HttpClient4Utils.createHttpClient(100, 20, 10000, 2000, 2000);public static HttpClient createHttpClient(int maxTotal, int maxPerRoute, int socketTimeout, int connectTimeout,int connectionRequestTimeout) {RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectionRequestTimeout).build();PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();cm.setMaxTotal(maxTotal);cm.setDefaultMaxPerRoute(maxPerRoute);CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(defaultRequestConfig).build();return httpClient;}
    
  3. 通过try-with-resources的写法,自动关闭资源。或者自己写try-catch-finally。

public static JSONObject httpGet(String url) throws HttpException {String[] strings = url.split("\\?");HttpUriRequestBase request = new HttpGet(strings[0] + "?" + UriEncoder.encode(strings[1]));try (CloseableHttpClient httpClient = getHttpClient();CloseableHttpResponse response = httpClient.execute(request)) {// ...return JSONObject.parseObject(responseContent);} catch (IOException e) {throw new HttpException(String.format("请求接口失败, url: %s", url), e);}}private static CloseableHttpClient getHttpClient() {return HttpClientBuilder.create().build();}

总结

官方的SDK最好写清楚使用,如果使用官方demo的情况下,出现服务性能下降的话,属实是无法接受的。

无论使用哪种sdk(服务端的sdk还是客户端的sdk),最好看下代码。尤其是新接入的,有条件的做下性能压测。

算是个老问题,用新形式踩坑了,挺有意思的。

再看看竞品的百度AI的:

https://ai.baidu.com/ai-doc/ANTIPORN/ik3h6xdze

参考文章

https://blog.csdn.net/qq_41999004/article/details/109141177

易盾sdk引起项目的整体耗时问题?相关推荐

  1. Android 接入网易易盾 SDK (文本检测)

    序 最近老不舒服了 . 直接开始 . 项目有个需求对学员在看直播发送的聊天消息进行监测 .大概意思就是需要进行管控 .选择了第三方 网易的易盾 . 看完文档发现这个易盾是服务端使用的 ,文档里面根本没 ...

  2. iOS接入网易易盾并实现

    一.接入网易易盾SDK 前言 1.此文章为Swift版本,OC版本可参考网易易盾官网接入实现文档 2.网易易盾SDK兼容iOS系统版本9.0+ CocoaPods集成方式 1.更新Podfile文件 ...

  3. 网易易盾,js逆向:★★★★★

    前言 可以关注我哟,一起学习,主页有更多练习例子 如果哪个练习我没有写清楚,可以留言我会补充 如果有加密的网站可以留言发给我,一起学习共享学习路程 如侵权,联系我,删 此文仅用于学习参考,请勿于商用, ...

  4. 网易易盾验证码移动端迎来新版本 开始支持智能无感知验证

    近日,网易易盾验证码移动端迎来新版本,该版本支持智能无感知验证码,对整体交互及性能等方面进行了优化和提升. 移动端智能无感知验证码交互流程图 验证码移动端最新版本开始支持智能无感知验证码,极致用户体验 ...

  5. 移动开发必须要了解的易盾加固生态

    本文由作者余宝虹授权网易云社区发布. 移动开发和服务端开发不一样,移动开发打包后的代码安装在用户的手机上,这样一来就为黑客提供了分析的便利,主要存在下面几个比较大的风险: 1 APK被逆向破解,去掉会 ...

  6. 网易云易盾朱星星:最容易被驳回的10大APP过检项

    欢迎访问网易云社区,了解更多网易技术产品运营经验. "走进网易:移动测试与安全实践"公开活动在杭州西湖区颐高创业大厦4F楼友会创业咖啡厅举行.本次活动的议题聚焦在如何实现应用的高效 ...

  7. “易+”开源网易易盾 GameSentry 正式开源,做游戏安全保障的尖兵利刃

    The First Line Of Defense - GameSentry 哨兵是守护安全的第一道防线,不停的监视着每一个外敌可能进行渗透的角落.网易易盾的 GameSentry 作为游戏安全战场上 ...

  8. PHP接入网易易盾验证码

    接入网易易盾验证码流程图: 后端接入:http://support.dun.163.com/documents/15588062143475712?docId=69218161355051008 前端 ...

  9. 网易易盾验证码移动端迎来新版本 开始支持智能无感知验证 1

    近日,网易易盾验证码移动端迎来新版本,该版本支持智能无感知验证码,对整体交互及性能等方面进行了优化和提升. 移动端智能无感知验证码交互流程图 验证码移动端最新版本开始支持智能无感知验证码,极致用户体验 ...

最新文章

  1. TextRNN用于文本分类
  2. .NET C#生成随机颜色,可以控制亮度,生成暗色或者亮色 基于YUV模式判断颜色明亮度...
  3. 网关流控利器:结合 AHAS 实现 Ingress/Nginx 流量控制
  4. Sidebar Enhancements使用说明
  5. web前端教程之JavaScript的作用域
  6. 【小题目】输入三个数字,获取三个数字中的最小值
  7. 160 - 20 BuLLeT.8
  8. Java虚拟机10:类加载器
  9. 毫秒级从百亿大表任意维度筛选数据,是怎么做到的…
  10. Linux下安装golang
  11. keepalive+nginx实现负载均衡高可用_高可用、负载均衡 集群部署方案:Keepalived + Nginx + Tomcat...
  12. 服务器维护委托合同,服务器维护委托合同样本.doc
  13. 采集51job职位数量画图后发邮件
  14. 被远程之后,键盘失灵问题
  15. 什么是计算机网络中域名,什么是域名?
  16. 浅谈认识商业智能过程中遇到的困难
  17. Android Camera 实时滤镜(五)
  18. 【高等数学】区间再现公式及其相关推论
  19. JUNOS的基本操作
  20. Moore FSM和Mealy FSM的区别

热门文章

  1. 知识点小悟2019-1-18
  2. 旋转图片验证码(识别/破解)解决(一)
  3. Golang优秀开源项目汇总(持续更新。。。)
  4. html添加实时视频的代码,关于添加网页视频的html代码
  5. 【MySQL】经典练习题(部门表、员工表、工资表)
  6. 智慧街道空间导引及创新平台
  7. 用计算机弹奏世末歌者,【UTAU用】世末歌者【非官方谱】
  8. FontMetrics
  9. python mqtt tls_某些程序中TLS证书上的MQTT(Mosquitto)错误
  10. 微信8.0,初心改不改?