大规模数据处理中拒绝连接错误分析处理

本文分析了Spark处理大规模数据集时出现的shuffle错误,包括MetadataFetchFailedException和FetchFailedException等问题,探讨了错误产生的原因及解决方案。

1、处理的数据有几百个G,把数据处理成按照手机号计算1万多个特征 ;

2、数据处理环境:

     spark-2.0.2;

    --executor-memory 40g --total-executor-cores 120 --driver-memory 40g  

3、报的错误

org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 774
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:6
95)
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:6
91)
        at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:733)
        at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
        at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:732)
        at org.apache.spark.MapOutputTracker$.org$apache$spark$MapOutputTracker$$convertMapStatuses(MapOutputTracker.scala:691)
        at org.apache.spark.MapOutputTracker.getMapSizesByExecutorId(MapOutputTracker.scala:145)
        at org.apache.spark.shuffle.BlockStoreShuffleReader.read(BlockStoreShuffleReader.scala:49)
        at org.apache.spark.sql.execution.ShuffledRowRDD.compute(ShuffledRowRDD.scala:169)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
        at org.apache.spark.scheduler.Task.run(Task.scala:86)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

)
18/03/14 01:15:18 INFO scheduler.TaskSetManager: Task 151.1 in stage 1595.0 (TID 179950) failed, but another instance of the task has alread
y succeeded, so not re-queuing the task to be re-executed.
18/03/14 01:15:18 INFO scheduler.DAGScheduler: Marking ShuffleMapStage 1595 (csv at FeatherAnalyseOnline.java:65) as failed due to a fetch f
ailure from ShuffleMapStage 1594 (csv at FeatherAnalyseOnline.java:65)
18/03/14 01:15:18 INFO scheduler.DAGScheduler: ShuffleMapStage 1595 (csv at FeatherAnalyseOnline.java:65) failed in 65.360 s
18/03/14 01:15:18 INFO scheduler.DAGScheduler: Resubmitting ShuffleMapStage 1594 (csv at FeatherAnalyseOnline.java:65) and ShuffleMapStage 1
595 (csv at FeatherAnalyseOnline.java:65) due to fetch failure
18/03/14 01:15:18 INFO storage.BlockManagerInfo: Added broadcast_890_piece0 in memory on 192.168.200.172:42101 (size: 24.3 KB, free: 21.3 GB
)
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 827 to 192.168.200.172:32868
18/03/14 01:15:18 INFO spark.MapOutputTrackerMaster: Size of output statuses for shuffle 827 is 1082 bytes
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 828 to 192.168.200.172:32868
18/03/14 01:15:18 INFO spark.MapOutputTrackerMaster: Size of output statuses for shuffle 828 is 3991 bytes
18/03/14 01:15:18 INFO scheduler.DAGScheduler: Resubmitting failed stages
18/03/14 01:15:18 INFO scheduler.TaskSetManager: Starting task 1.0 in stage 1516.1 (TID 179953, 192.168.200.168, partition 49, PROCESS_LOCAL
, 5699 bytes)
18/03/14 01:15:18 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 179953 on executor id: 6 hostname: 192.168.200.1
68.
18/03/14 01:15:18 WARN scheduler.TaskSetManager: Lost task 70.1 in stage 1595.0 (TID 179951, 192.168.200.168): FetchFailed(null, shuffleId=7
74, mapId=-1, reduceId=70, message=
org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 774
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:6
95)
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:6
91)
        at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:733)
        at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
        at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:732)
        at org.apache.spark.MapOutputTracker$.org$apache$spark$MapOutputTracker$$convertMapStatuses(MapOutputTracker.scala:691)
        at org.apache.spark.MapOutputTracker.getMapSizesByExecutorId(MapOutputTracker.scala:145)
        at org.apache.spark.shuffle.BlockStoreShuffleReader.read(BlockStoreShuffleReader.scala:49)
        at org.apache.spark.sql.execution.ShuffledRowRDD.compute(ShuffledRowRDD.scala:169)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
        at org.apache.spark.scheduler.Task.run(Task.scala:86)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

)
18/03/14 01:15:18 INFO scheduler.TaskSetManager: Task 70.1 in stage 1595.0 (TID 179951) failed, but another instance of the task has already
 succeeded, so not re-queuing the task to be re-executed.
18/03/14 01:15:18 INFO scheduler.DAGScheduler: Resubmitting ShuffleMapStage 1594 (csv at FeatherAnalyseOnline.java:65) and ShuffleMapStage 1
595 (csv at FeatherAnalyseOnline.java:65) due to fetch failure
18/03/14 01:15:18 INFO storage.BlockManagerInfo: Added broadcast_890_piece0 in memory on 192.168.200.168:41381 (size: 24.3 KB, free: 21.2 GB
)
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 827 to 192.168.200.168:59086
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 828 to 192.168.200.168:59086
18/03/14 01:15:18 INFO scheduler.DAGScheduler: Resubmitting failed stages
18/03/14 01:15:18 INFO storage.BlockManagerInfo: Added broadcast_884_piece0 in memory on 192.168.200.165:36764 (size: 8.3 KB, free: 19.7 GB)
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 145 to 192.168.200.169:52372
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 145 to 192.168.200.170:48698
18/03/14 01:15:18 INFO spark.MapOutputTrackerMaster: Size of output statuses for shuffle 145 is 4770 bytes
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 145 to 192.168.200.175:47132
18/03/14 01:15:18 INFO scheduler.TaskSetManager: Starting task 2.0 in stage 1516.1 (TID 179954, 192.168.200.168, partition 64, PROCESS_LOCAL
, 5699 bytes)
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 145 to 192.168.200.172:32868
18/03/14 01:15:18 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 145 to 192.168.200.166:44376
18/03/14 01:15:18 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 179954 on executor id: 6 hostname: 192.168.200.1
68.
18/03/14 01:15:18 WARN scheduler.TaskSetManager: Lost task 40.0 in stage 1595.0 (TID 178297, 192.168.200.168): FetchFailed(BlockManagerId(4,
 192.168.200.164, 34412), shuffleId=774, mapId=50, reduceId=40, message=
org.apache.spark.shuffle.FetchFailedException: Failed to connect to /192.168.200.164:34412
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.throwFetchFailedException(ShuffleBlockFetcherIterator.scala:357)
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.next(ShuffleBlockFetcherIterator.scala:332)
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.next(ShuffleBlockFetcherIterator.scala:54)
        at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
        at scala.collection.Iterator$$anon$12.nextCur(Iterator.scala:434)
        at scala.collection.Iterator$$anon$12.hasNext(Iterator.scala:440)
        at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
        at org.apache.spark.util.CompletionIterator.hasNext(CompletionIterator.scala:32)
        at org.apache.spark.InterruptibleIterator.hasNext(InterruptibleIterator.scala:39)
        at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
        at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.agg_doAggregateWithKeys$(Unknown Source)
        at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
        at org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43)
        at org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8$$anon$1.hasNext(WholeStageCodegenExec.scala:370)
        at org.apache.spark.sql.execution.aggregate.HashAggregateExec$$anonfun$doExecute$1$$anonfun$4.apply(HashAggregateExec.scala:96)
        at org.apache.spark.sql.execution.aggregate.HashAggregateExec$$anonfun$doExecute$1$$anonfun$4.apply(HashAggregateExec.scala:94)
        at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:785)
        at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:785)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
        at org.apache.spark.scheduler.Task.run(Task.scala:86)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Failed to connect to /192.168.200.164:34412
        at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:228)
        at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:179)
        at org.apache.spark.network.netty.NettyBlockTransferService$$anon$1.createAndStart(NettyBlockTransferService.scala:96)
        at org.apache.spark.network.shuffle.RetryingBlockFetcher.fetchAllOutstanding(RetryingBlockFetcher.java:140)
        at org.apache.spark.network.shuffle.RetryingBlockFetcher.access$200(RetryingBlockFetcher.java:43)
        at org.apache.spark.network.shuffle.RetryingBlockFetcher$1.run(RetryingBlockFetcher.java:170)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        ... 3 more
Caused by: java.net.ConnectException: Connection refused: /192.168.200.164:34412
        at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
        at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
        at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:224)
        at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:289)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
        ... 1 more

)

4、报错的处理;

shuffle分为shuffle writeshuffle read两部分。 
shuffle write的分区数由上一阶段的RDD分区数控制,shuffle read的分区数则是由Spark提供的一些参数控制。

shuffle write可以简单理解为类似于saveAsLocalDiskFile的操作,将计算的中间结果按某种规则临时放到各个executor所在的本地磁盘上。

shuffle read的时候数据的分区数则是由spark提供的一些参数控制。可以想到的是,如果这个参数值设置的很小,同时shuffle read的量很大,那么将会导致一个task需要处理的数据非常大。结果导致JVM crash,从而导致取shuffle数据失败,同时executor也丢失了,看到Failed to connect to host的错误,也就是executor lost的意思。有时候即使不会导致JVM crash也会造成长时间的gc。

解决办法

知道原因后问题就好解决了,主要从shuffle的数据量和处理shuffle数据的分区数两个角度入手。

  1. 减少shuffle数据

    思考是否可以使用map side join或是broadcast join来规避shuffle的产生。

    将不必要的数据在shuffle前进行过滤,比如原始数据有20个字段,只要选取需要的字段进行处理即可,将会减少一定的shuffle数据。

  2. SparkSQL和DataFrame的join,group by等操作

    通过spark.sql.shuffle.partitions控制分区数,默认为200,根据shuffle的量以及计算的复杂度提高这个值。

  3. Rdd的join,groupBy,reduceByKey等操作

    通过spark.default.parallelism控制shuffle read与reduce处理的分区数,默认为运行任务的core的总数(mesos细粒度模式为8个,local模式为本地的core总数),官方建议为设置成运行任务的core的2-3倍。

  4. 提高executor的内存

    通过spark.executor.memory适当提高executor的memory值。

  5. 是否存在数据倾斜的问题

    空值是否已经过滤?异常数据(某个key数据特别大)是否可以单独处理?考虑改变数据分区规则。

org.apache.spark.storage.BlockNotFoundException: Block broadcast_938_piece1 not found
        at org.apache.spark.storage.BlockManager.getBlockData(BlockManager.scala:287)
        at org.apache.spark.network.netty.NettyBlockRpcServer$$anonfun$2.apply(NettyBlockRpcServer.scala:60)
        at org.apache.spark.network.netty.NettyBlockRpcServer$$anonfun$2.apply(NettyBlockRpcServer.scala:60)
        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
        at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
        at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
        at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:186)
        at org.apache.spark.network.netty.NettyBlockRpcServer.receive(NettyBlockRpcServer.scala:60)
        at org.apache.spark.network.server.TransportRequestHandler.processRpcRequest(TransportRequestHandler.java:159)
        at org.apache.spark.network.server.TransportRequestHandler.handle(TransportRequestHandler.java:107)
        at org.apache.spark.network.server.TransportChannelHandler.channelRead0(TransportChannelHandler.java:119)
        at org.apache.spark.network.server.TransportChannelHandler.channelRead0(TransportChannelHandler.java:51)
        at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
        at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
        at org.apache.spark.network.util.TransportFrameDecoder.channelRead(TransportFrameDecoder.java:85)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
        at java.lang.Thread.run(Thread.java:745)
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Starting task 100.0 in stage 1947.0 (TID 1419450, 192.168.200.167, partition 100, PROCESS_LOCAL, 5371 bytes)
18/03/14 13:57:30 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 1419450 on executor id: 8 hostname: 192.168.200.167.
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Finished task 70.0 in stage 1955.0 (TID 1419178) in 7460 ms on 192.168.200.167 (413/1000)
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Starting task 101.0 in stage 1947.0 (TID 1419451, 192.168.200.175, partition 101, PROCESS_LOCAL, 5371 bytes)
18/03/14 13:57:30 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 1419451 on executor id: 1 hostname: 192.168.200.175.
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Finished task 183.0 in stage 1951.0 (TID 1419137) in 7627 ms on 192.168.200.175 (698/1000)
18/03/14 13:57:30 INFO spark.MapOutputTrackerMasterEndpoint: Asked to send map output locations for shuffle 477 to 192.168.200.166:60518
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Starting task 102.0 in stage 1947.0 (TID 1419452, 192.168.200.166, partition 102, PROCESS_LOCAL, 5371 bytes)
18/03/14 13:57:30 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 1419452 on executor id: 4 hostname: 192.168.200.166.
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Starting task 103.0 in stage 1947.0 (TID 1419453, 192.168.200.171, partition 103, PROCESS_LOCAL, 5371 bytes)
18/03/14 13:57:30 INFO cluster.CoarseGrainedSchedulerBackend$DriverEndpoint: Launching task 1419453 on executor id: 3 hostname: 192.168.200.171.
18/03/14 13:57:30 INFO scheduler.TaskSetManager: Finished task 59.0 in stage 1955.0 (TID 1419158) in 7693 ms on 192.168.200.171 (414/1000)
18/03/14 13:57:30 WARN scheduler.TaskSetManager: Lost task 1.0 in stage 1947.0 (TID 1419351, 192.168.200.166): java.io.IOException: org.apache.spark.SparkException: Failed to get broadcast_938_piece1 of broadcast_938
        at org.apache.spark.util.Utils$.tryOrIOException(Utils.scala:1283)
        at org.apache.spark.broadcast.TorrentBroadcast.readBroadcastBlock(TorrentBroadcast.scala:174)
        at org.apache.spark.broadcast.TorrentBroadcast._value$lzycompute(TorrentBroadcast.scala:65)
        at org.apache.spark.broadcast.TorrentBroadcast._value(TorrentBroadcast.scala:65)
        at org.apache.spark.broadcast.TorrentBroadcast.getValue(TorrentBroadcast.scala:89)
        at org.apache.spark.broadcast.Broadcast.value(Broadcast.scala:70)
        at org.apache.spark.MapOutputTracker$$anonfun$deserializeMapStatuses$1.apply(MapOutputTracker.scala:661)
        at org.apache.spark.MapOutputTracker$$anonfun$deserializeMapStatuses$1.apply(MapOutputTracker.scala:661)
        at org.apache.spark.internal.Logging$class.logInfo(Logging.scala:54)
        at org.apache.spark.MapOutputTracker$.logInfo(MapOutputTracker.scala:598)
        at org.apache.spark.MapOutputTracker$.deserializeMapStatuses(MapOutputTracker.scala:660)
        at org.apache.spark.MapOutputTracker.getStatuses(MapOutputTracker.scala:203)
        at org.apache.spark.MapOutputTracker.getMapSizesByExecutorId(MapOutputTracker.scala:142)
        at org.apache.spark.shuffle.BlockStoreShuffleReader.read(BlockStoreShuffleReader.scala:49)
        at org.apache.spark.sql.execution.ShuffledRowRDD.compute(ShuffledRowRDD.scala:169)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
        at org.apache.spark.scheduler.Task.run(Task.scala:86)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.spark.SparkException: Failed to get broadcast_938_piece1 of broadcast_938
        at org.apache.spark.broadcast.TorrentBroadcast$$anonfun$org$apache$spark$broadcast$TorrentBroadcast$$readBlocks$1.apply$mcVI$sp(TorrentBroadcast.scala:146)
        at org.apache.spark.broadcast.TorrentBroadcast$$anonfun$org$apache$spark$broadcast$TorrentBroadcast$$readBlocks$1.apply(TorrentBroadcast.scala:125)
        at org.apache.spark.broadcast.TorrentBroadcast$$anonfun$org$apache$spark$broadcast$TorrentBroadcast$$readBlocks$1.apply(TorrentBroadcast.scala:125)
        at scala.collection.immutable.List.foreach(List.scala:381)
        at org.apache.spark.broadcast.TorrentBroadcast.org$apache$spark$broadcast$TorrentBroadcast$$readBlocks(TorrentBroadcast.scala:125)
        at org.apache.spark.broadcast.TorrentBroadcast$$anonfun$readBroadcastBlock$1.apply(TorrentBroadcast.scala:186)
        at org.apache.spark.util.Utils$.tryOrIOException(Utils.scala:1276)
        ... 32 more



org.apache.spark.shuffle.FetchFailedException: Failed to send RPC 6572748480227485466 to /192.168.200.164:44766: java.nio.channels.ClosedChannelException
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.throwFetchFailedException(ShuffleBlockFetcherIterator.scala:357)
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.next(ShuffleBlockFetcherIterator.scala:332)
        at org.apache.spark.storage.ShuffleBlockFetcherIterator.next(ShuffleBlockFetcherIterator.scala:54)
        at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
        at scala.collection.Iterator$$anon$12.nextCur(Iterator.scala:434)
        at scala.collection.Iterator$$anon$12.hasNext(Iterator.scala:440)
        at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
        at org.apache.spark.util.CompletionIterator.hasNext(CompletionIterator.scala:32)
        at org.apache.spark.InterruptibleIterator.hasNext(InterruptibleIterator.scala:39)
        at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408)
        at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.agg_doAggregateWithKeys$(Unknown Source)
        at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
        at org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43)
        at org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8$$anon$1.hasNext(WholeStageCodegenExec.scala:370)
        at org.apache.spark.sql.execution.aggregate.HashAggregateExec$$anonfun$doExecute$1$$anonfun$4.apply(HashAggregateExec.scala:96)
        at org.apache.spark.sql.execution.aggregate.HashAggregateExec$$anonfun$doExecute$1$$anonfun$4.apply(HashAggregateExec.scala:94)
        at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:785)
        at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:785)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:319)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:283)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
        at org.apache.spark.scheduler.Task.run(Task.scala:86)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Failed to send RPC 6572748480227485466 to /192.168.200.164:44766: java.nio.channels.ClosedChannelException
        at org.apache.spark.network.client.TransportClient$3.operationComplete(TransportClient.java:249)
        at org.apache.spark.network.client.TransportClient$3.operationComplete(TransportClient.java:233)
        at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:680)
        at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:603)
        at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:563)
        at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:424)
        at io.netty.channel.ChannelOutboundBuffer.safeFail(ChannelOutboundBuffer.java:678)
        at io.netty.channel.ChannelOutboundBuffer.remove0(ChannelOutboundBuffer.java:298)
        at io.netty.channel.ChannelOutboundBuffer.failFlushed(ChannelOutboundBuffer.java:621)
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:589)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.close(DefaultChannelPipeline.java:1107)
        at io.netty.channel.AbstractChannelHandlerContext.invokeClose(AbstractChannelHandlerContext.java:543)
        at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:528)
        at io.netty.channel.ChannelOutboundHandlerAdapter.close(ChannelOutboundHandlerAdapter.java:71)
        at io.netty.channel.AbstractChannelHandlerContext.invokeClose(AbstractChannelHandlerContext.java:543)
        at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:528)
        at io.netty.channel.ChannelDuplexHandler.close(ChannelDuplexHandler.java:73)
        at io.netty.channel.AbstractChannelHandlerContext.invokeClose(AbstractChannelHandlerContext.java:543)
        at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:528)
        at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:394)
        at org.apache.spark.network.server.TransportChannelHandler.userEventTriggered(TransportChannelHandler.java:147)
        at io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:279)
        at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:265)
        at io.netty.handler.timeout.IdleStateHandler.channelIdle(IdleStateHandler.java:340)
        at io.netty.handler.timeout.IdleStateHandler$AllIdleTimeoutTask.run(IdleStateHandler.java:455)
        at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:120)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:357)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
        ... 1 more
Caused by: java.nio.channels.ClosedChannelException

)

<think>我们正在处理大规模数据(每表可能数百万行),需要将数据分批(每批1万行)调用第三方接口并存入第三方库。使用多线程提高效率。 关键挑战:如何设置线程池参数(核心线程数、最大线程数、队列容量、空闲线程存活时间等)以达到最佳性能,同时避免资源耗尽。 参考引用: [^1]:提到大数据处理中的JVM调优,特别是内存管理、GC行为和线程调度。 [^2][^3][^4][^5]:涉及线程池参数调优,包括核心线程数、最大线程数、队列、keepAliveTime等。 结合引用内容,我们分析线程池参数调优的最佳实践: 1. 线程池核心参数: - corePoolSize(核心线程数):线程池中保持活动状态的最小线程数,即使它们处于空闲状态。 - maximumPoolSize(最大线程数):线程池中允许存在的最大线程数。 - workQueue(工作队列):用于保存等待执行的任务的阻塞队列。 - keepAliveTime(空闲线程存活时间):当线程数大于核心线程数时,多余的空闲线程在终止前等待新任务的最长时间。 2. 线程池工作流程(引用[3][4]): - 当新任务提交时: a. 如果当前运行的线程数小于corePoolSize,则创建新线程处理任务(即使有空闲线程)。 b. 如果当前运行的线程数等于或大于corePoolSize,则将任务加入workQueue。 c. 如果队列已满,且当前线程数小于maximumPoolSize,则创建新线程处理任务。 d. 如果队列已满,且当前线程数等于maximumPoolSize,则根据拒绝策略处理。 3. 调优目标: - 最大化资源利用率(CPU、内存、网络) - 避免系统过载(如OOM、线程数过多导致上下文切换频繁) - 减少任务等待时间,提高吞吐量 4. 调优实践: a. 核心线程数(corePoolSize): - 通常设置为CPU密集型任务:CPU核数+1;I/O密集型任务:2*CPU核数(或更多)。 - 在大规模数据处理中,我们的任务主要是I/O密集型(网络I/O:调用第三方接口;磁盘I/O:数据库读写)。 - 建议初始值:CPU核数 * (1 + (I/O等待时间/CPU计算时间))。由于I/O等待时间通常较长,可以设置较大的核心线程数。 - 例如:假设服务器CPU核数为N,则初始设置:corePoolSize = 2*N 或 4*N(根据实际情况调整)。 b. 最大线程数(maximumPoolSize): - 设置上限以防止资源耗尽。考虑因素: * 系统资源:内存(每个线程需要栈空间,默认1MB,可通过-Xss调整)、文件句柄数、数据库连接池大小等。 * 第三方接口的承受能力(避免瞬间大量请求压垮第三方)。 - 建议:根据系统资源设置一个上限,例如:4*N ~ 10*N(N为CPU核数)。同时,监控线程数峰值,调整。 c. 工作队列(workQueue): - 队列类型:常用有界队列(如ArrayBlockingQueue)和无界队列(如LinkedBlockingQueue)。无界队列可能导致OOM,因此推荐有界队列。 - 队列容量:需要权衡。队列太小可能导致频繁创建新线程(达到最大线程数后触发拒绝策略);队列太大可能增加延迟,且占用内存。 - 建议:根据任务执行时间和可接受延迟来设置。例如,如果任务执行时间较长,队列容量可设置大些(如1000~10000),但必须监控队列堆积情况。 d. 空闲线程存活时间(keepAliveTime): - 当线程数大于corePoolSize时,空闲线程在等待新任务超过keepAliveTime后会被终止。 - 在任务量波动大的场景,设置合理的keepAliveTime可以动态调整线程数,避免长期空闲占用资源。 - 建议:设置一个适中的值,如60秒(根据任务提交频率调整)。 e. 拒绝策略(RejectedExecutionHandler): - 当线程池和队列都饱和时,新任务会被拒绝。 - 常用策略: * AbortPolicy(默认):抛出RejectedExecutionException * CallerRunsPolicy:由提交任务的线程直接执行 * DiscardPolicy:直接丢弃任务 * DiscardOldestPolicy:丢弃队列中最旧的任务,然后重试提交 - 在大数据处理中,建议使用CallerRunsPolicy,避免任务丢失,但会降低提交速度。 5. 监控与动态调整: - 使用Spring Boot Actuator监控线程池状态(活动线程数、队列大小等)。 - 根据监控数据调整参数: * 如果队列经常满,且线程数达到最大,考虑增加maximumPoolSize或队列容量(但需注意系统资源上限)。 * 如果CPU利用率低,但任务积压,可能是I/O等待时间长,可适当增加线程数。 * 如果GC频繁或内存占用高,检查队列是否过大,适当减小队列容量。 6. JVM调优配合(引用[1]): - 堆内存设置:大数据处理需要足够内存,根据数据批次大小调整-Xms和-Xmx。 - 垃圾回收器选择:低延迟应用可选G1或ZGC,减少GC停顿时间。 - 线程栈大小:如果线程数多,可适当减小-Xss(如256k),但需确保不出现StackOverflowError。 7. 实际场景建议: - 假设我们每批处理1万行,调用第三方接口和存储可能需要几百毫秒到几秒(I/O密集型)。 - 服务器配置:8核CPU,32GB内存,Oracle数据库连接池最大100。 - 初始线程池配置: corePoolSize = 8(CPU核数) maximumPoolSize = 32(4倍核数) workQueue = ArrayBlockingQueue(1000) // 有界队列,容量1000 keepAliveTime = 60秒 rejectedExecutionHandler = CallerRunsPolicy() - 运行监控: * 观察线程池活动线程数:如果长期在32(最大线程数)附近,且队列满,考虑增加最大线程数或队列容量(但注意数据库连接池限制)。 * 观察CPU利用率:如果低于70%,可尝试增加线程数;如果接近100%,则不宜再增加。 * 观察内存使用:避免频繁Full GC。 8. 注意事项: - 数据库连接池大小:线程池大小不应超过数据库连接池大小(否则线程会因等待数据库连接而阻塞)。 - 第三方接口限流:如果第三方接口有QPS限制,需在客户端控制请求速率(如使用RateLimiter)。 - 错误重试机制:对于失败的批次,应有重试机制(如记录失败批次,稍后重试)。 9. 调优总结: - 没有万能配置,需要根据实际场景测试、监控、调整。 - 核心公式:线程数 ≈ CPU核数 * (1 + 等待时间/计算时间) [等待时间主要指I/O等待] - 在I/O密集型任务中,可适当增加线程数,但必须监控系统资源。 相关代码调整(线程池配置): 根据以上分析,修改之前的线程池配置: ```java @Configuration @EnableAsync public class AsyncConfig { @Value("${thread.pool.core.size:8}") private int corePoolSize; @Value("${thread.pool.max.size:32}") private int maxPoolSize; @Value("${thread.pool.queue.capacity:1000}") private int queueCapacity; @Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(60); // 空闲线程存活时间 executor.setThreadNamePrefix("DataProcessor-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } } ``` 在application.yml中配置参数: ```yaml thread: pool: core: size: 8 max: size: 32 queue: capacity: 1000 ``` 通过以上配置,我们可以在不同环境中调整参数。 §§相关问题§§ 1. 如何监控线程池的运行状态? 2. 线程池调优中如何避免数据库连接池成为瓶颈? 3. 第三方接口调用频率过高被限流如何处理? 4. 如何确定任务执行是CPU密集型还是I/O密集型? 5. 线程池参数调优后如何进行压测验证?</think>### 大规模数据处理中线程池参数调优的最佳实践 在大规模数据处理场景(如每表数百万行数据分批次处理)中,线程池参数调优需平衡吞吐量、资源利用率和系统稳定性。以下是基于引用内容的最佳实践总结: #### 1. 核心参数调优策略 ```mermaid graph LR A[线程池参数] --> B[核心线程数] A --> C[最大线程数] A --> D[队列容量] A --> E[KeepAliveTime] A --> F[拒绝策略] ``` 1. **核心线程数 (corePoolSize)** - **CPU密集型任务**:设置为 `CPU核数 + 1` ```java int coreSize = Runtime.getRuntime().availableProcessors() + 1; ``` - **I/O密集型任务**(如网络请求/DB操作):设置为 `2 * CPU核数` [^4][^5] - **大规模数据处理建议**:初始值设为 `CPU核数 × 2`,根据实际负载动态调整 2. **最大线程数 (maximumPoolSize)** - 关键公式:`maxSize = coreSize + 预期峰值任务量 / 单任务处理时间` - 硬限制需考虑: - JVM堆内存(每个线程约需1MB栈空间) - 数据库连接池大小 - 第三方接口并发承受能力 - **推荐值**:`4 * CPU核数` ~ `10 * CPU核数` [^2][^4] 3. **队列容量 (queueCapacity)** - 队列类型选择: - `SynchronousQueue`:无缓冲,直接移交(适合短任务) - `LinkedBlockingQueue`:无界队列(易导致OOM) - **推荐**:`ArrayBlockingQueue` 有界队列 - 容量计算公式: ``` 队列容量 = (最大预期延迟 × 核心线程处理能力) / 单任务耗时 ``` - **实践建议**:初始值设为 `1000-5000`,监控队列堆积率调整[^3][^4] 4. **KeepAliveTime** - 定义:非核心线程空闲存活时间 - **调优原则**: - 任务波动大时:设置 `30-60秒` 回收多余线程 - 稳定高负载场景:设为 `0`(避免频繁创建销毁) - 注意:不影响核心线程[^3][^5] 5. **拒绝策略 (RejectedExecutionHandler)** - 大数据处理推荐: ```java new ThreadPoolExecutor.CallerRunsPolicy() // 由提交线程直接执行 ``` - 其他策略: - `AbortPolicy`(默认):抛出异常 - `DiscardPolicy`:静默丢弃 - `DiscardOldestPolicy`:丢弃队列最旧任务 #### 2. 配置示例(Spring Boot) ```java @Bean("dataProcessorExecutor") public Executor taskExecutor() { int cores = Runtime.getRuntime().availableProcessors(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(cores * 2); // 核心线程数 executor.setMaxPoolSize(cores * 8); // 最大线程数 executor.setQueueCapacity(2000); // 有界队列 executor.setKeepAliveSeconds(30); // 空闲线程存活时间 executor.setThreadNamePrefix("DP-"); executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); executor.initialize(); return executor; } ``` #### 3. 性能优化技巧 1. **动态调参** 通过JMX实时调整参数: ```java ThreadPoolTaskExecutor executor = context.getBean(...); executor.setCorePoolSize(newSize); // 运行时调整 ``` 2. **资源隔离** - 区分处理线程池和IO线程池 - 为AC82/AC83表分配独立线程池 3. **背压控制** 监控队列堆积率触发流控: ```java if(executor.getThreadPoolExecutor().getQueue().size() > 1000) { // 暂停新任务提交 } ``` 4. **JVM配合调优**[^1] - 设置合理堆内存:`-Xms4g -Xmx8g` - 调整线程栈:`-Xss256k`(减少内存占用) - 使用G1垃圾回收器:`-XX:+UseG1GC` #### 4. 监控关键指标 | 指标 | 健康值 | 监控工具 | |---------------------|---------------------|-------------------| | 活跃线程数 | 70-80% maxPoolSize | Spring Boot Actuator | | 队列使用率 | <80% capacity | Micrometer | | 任务拒绝率 | <0.1% | Prometheus | | 平均任务耗时 | 小于批次间隔 | ELK | #### 5. 异常处理 ```java executor.setRejectedExecutionHandler((r, e) -> { log.warn("任务被拒绝,存入重试队列"); retryQueue.add(r); // 自定义重试逻辑 }); ``` > **最佳实践总结**:初始按 `(核心=2N, 最大=8N, 队列=2000)` 配置,通过监控逐步调整。高负载场景优先增加队列容量,超高负载才增加最大线程数[^2][^4]。定期进行压力测试,观察GC行为和CPU利用率[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值