1. Maintaining a large number of copy problems caused by Bytebuf.
If careful children’s shoes will find io.net ty handler. Codec. ByteToMessageDecoder he offers a buffer. So the buffer means there’s a copy.
Netty provides two ways:
public static final Cumulator MERGE_CUMULATOR = new Cumulator() {
@Override
public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
try {
final ByteBuf buffer;
// 1. If the original write space is insufficient c.w+in.r>c.capacity
if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes()
|| cumulation.refCnt() > 1 || cumulation.isReadOnly()) {
// Create a new object. Step 2 copy the original, step 3 release the original.
buffer = expandCumulation(alloc, cumulation, in.readableBytes());
} else {
//2. Otherwise the space is satisfied. No need to extend
buffer = cumulation;
}
// 3. Copy the newly written data to the buffer
buffer.writeBytes(in);
/ / return
return buffer;
} finally {
// Release the objectin.release(); }}};Copy the code
We found that the first implementation brought us a lot of copies. But we can provide a CompositeByteBuf. There’s actually no copy. I’m just trying to keep them together.
So you have fewer copies.
public static final Cumulator COMPOSITE_CUMULATOR = new Cumulator() {
@Override
public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
ByteBuf buffer;
try {
// If the reference is greater than 1, there is a problem. Will help us build a new one.
if (cumulation.refCnt() > 1) {
buffer = expandCumulation(alloc, cumulation, in.readableBytes());
/ / write.
buffer.writeBytes(in);
} else {
CompositeByteBuf composite;
if (cumulation instanceof CompositeByteBuf) {
composite = (CompositeByteBuf) cumulation;
} else {
composite = alloc.compositeBuffer(Integer.MAX_VALUE);
composite.addComponent(true, cumulation);
}
// Add it directly
composite.addComponent(true, in);
// Cancel the reference, even though it is empty. But it didn't. It just put in the original composite.
in = null;
buffer = composite;
}
return buffer;
} finally {
// To prevent the first case. Finally unified processing.
if(in ! =null) { in.release(); }}}};Copy the code
But there is a problem with this. It’s serious. It’s a memory leak. Why?
The above code is a good example of why this is not recommended. Because there are a lot of problems. Objects can be freed even if they cannot be freed. However, users need to manually operate. This is very speechless. So basic do not recommend to use. But in fact also can, need you to judge artificially. Ha ha ha, in fact, how to say that it is difficult to maintain unity. That’s this right here.
So the root cause of this problem cannot be dealt with at present.
Netty how to solve the NIO idling problem.
For now, we’ll just consider NIOEventLoop
First look at io.net ty. Channel. Nio. NioEventLoop this class. The run method of.
@Override
protected void run(a) {
for (;;) {
try {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// Direct is not supported. Switch to the next step.
// fall-through to SELECT since the busy-wait is not supported with NIO
// The main code is here.
case SelectStrategy.SELECT:
// This is the select method.
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:}}catch (IOException e) {
rebuildSelector0();
handleLoopException(e);
continue;
}
// Handle events and so on.}}Copy the code
Let’s look at what select() does
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
int selectCnt = 0;
long currentTimeNanos = System.nanoTime();
// The maximum polling time is calculated. For example, IF I only let you poll 10 times, that is +10S. The concrete implementation can be seen.
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
for (;;) {
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
// If the polling time exceeds the maximum polling time. You can't keep polling.
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
selector.selectNow();
selectCnt = 1;
}
break;
}
if (hasTasks() && wakenUp.compareAndSet(false.true)) {
selector.selectNow();
selectCnt = 1;
break;
}
// The main thing that has changed is here. I'm going to change it to non-blocking, which is no argument by default. Possible bugs. So timeout blocking instead. Here is 1000 ms
int selectedKeys = selector.select(timeoutMillis);
selectCnt ++;
// If selectorKey is not empty. Will break
if(selectedKeys ! =0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
break;
}
if (Thread.interrupted()) {
// ...
selectCnt = 1;
break;
}
long time = System.nanoTime();
// If the wait does run out. Reset the count, otherwise it's a bug if it's less than that, keep polling, and if you keep bugging, create a new selector when it gets to 512. Create a new registry from the original registry.
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
// reset. Continue polling
selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// Here are the processes that exceed the threshold. I'm just going to rebuild it.
selector = selectRebuildSelector(selectCnt);
selectCnt = 1;
break;
}
//
currentTimeNanos = time;
}
// The loop ends.
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS) {
/ / log}}catch (CancelledKeyException e) {
/ / log}}Copy the code
How does Netty handle idling?
Step 1: Determine a maximum waiting time. The default is SCHEDULE_PURGE_INTERVAL=1s, that is, only 1s polling. Card dead.
Step 2: We call int selectedKeys = selector. Select (timeoutMillis); This will return within the timeout period in three cases:
1). If there is data then we can return it directly.
2). If there is no data and the wait times out, the polling will continue. The default timeoutMillis is (selectDeadLineNanos – currentTimeNanos + 500000L) / 1000000L let’s see why this is the case. The default is 1000ms. So if I’m actually waiting for a timeout, this is going to jump out.
- If there is a BUG, a idling BUG, then at this point
selectCnt++
Continuous execution. Triggers a threshold. This threshold can be passedSystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
This parameter setting, which defaults to a threshold of 512, resolves the BUG. At this point it will create a new Selector object. So that’s settled,
That’s why Netty doesn’t use our traditional programming methods.
while (true) {
// There is a BUG in idling.
selector.select();
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
// Register events
// Finally removekeyIterator.remove(); }}Copy the code
The above code will have Java NIO idle problem, so the mainstream implementation is Netty set. The bottom line is that Java’s underlying APIS are the problem, and Java is passing the buck to the underlying implementations of different platforms. This layer of delegation, iteration after iteration, is dead. Hahaha, let’s deal with it ourselves.
Can look at this article: www.cnblogs.com/JAYIT/p/824…