isThreadLocalConnMode
This commit is contained in:
@@ -12,7 +12,6 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.*;
|
import java.util.concurrent.atomic.*;
|
||||||
import org.redkale.annotation.ResourceType;
|
import org.redkale.annotation.ResourceType;
|
||||||
import org.redkale.net.client.*;
|
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,14 +99,6 @@ public class AsyncIOGroup extends AsyncGroup {
|
|||||||
return new AsyncIOThread(g, name, index, threads, workExecutor, safeBufferPool);
|
return new AsyncIOThread(g, name, index, threads, workExecutor, safeBufferPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AsyncIOThread createClientReadIOThread(ThreadGroup g, String name, int index, int threads, ExecutorService workExecutor, ByteBufferPool safeBufferPool) throws IOException {
|
|
||||||
return new ClientReadIOThread(g, name, index, threads, workExecutor, safeBufferPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AsyncIOThread createClientWriteIOThread(ThreadGroup g, String name, int index, int threads, ExecutorService workExecutor, ByteBufferPool safeBufferPool) throws IOException {
|
|
||||||
return new ClientWriteIOThread(g, name, index, threads, workExecutor, safeBufferPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncIOThread connectThread() {
|
AsyncIOThread connectThread() {
|
||||||
if (connectThreadInited.compareAndSet(false, true)) {
|
if (connectThreadInited.compareAndSet(false, true)) {
|
||||||
this.connectThread.start();
|
this.connectThread.start();
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ public abstract class Client<C extends ClientConnection<R, P>, R extends ClientR
|
|||||||
protected int readTimeoutSeconds;
|
protected int readTimeoutSeconds;
|
||||||
|
|
||||||
protected int writeTimeoutSeconds;
|
protected int writeTimeoutSeconds;
|
||||||
|
//------------------ LocalThreadMode模式 ------------------
|
||||||
|
|
||||||
|
final ThreadLocal<C> localConnection = new ThreadLocal();
|
||||||
|
|
||||||
//------------------ 可选项 ------------------
|
//------------------ 可选项 ------------------
|
||||||
//PING心跳的请求数据,为null且pingInterval<1表示不需要定时ping
|
//PING心跳的请求数据,为null且pingInterval<1表示不需要定时ping
|
||||||
@@ -263,6 +266,14 @@ public abstract class Client<C extends ClientConnection<R, P>, R extends ClientR
|
|||||||
return conn.writeChannel(request, respTransfer);
|
return conn.writeChannel(request, respTransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//是否采用ThreadLocal连接池模式
|
||||||
|
//支持ThreadLocal连接池模式的最基本要求:
|
||||||
|
// 1) 只能调用connect()获取连接,不能调用connect(SocketAddress addr)
|
||||||
|
// 2) request必须一次性输出,不能出现写入request后request.isCompleted()=false的情况
|
||||||
|
protected boolean isThreadLocalConnMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private C createConnection(int index, AsyncConnection channel) {
|
private C createConnection(int index, AsyncConnection channel) {
|
||||||
C conn = createClientConnection(index, channel);
|
C conn = createClientConnection(index, channel);
|
||||||
if (!channel.isReadPending()) {
|
if (!channel.isReadPending()) {
|
||||||
@@ -272,6 +283,36 @@ public abstract class Client<C extends ClientConnection<R, P>, R extends ClientR
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected CompletableFuture<C> connect() {
|
protected CompletableFuture<C> connect() {
|
||||||
|
if (isThreadLocalConnMode()) {
|
||||||
|
C conn = localConnection.get();
|
||||||
|
if (conn == null || !conn.isOpen()) {
|
||||||
|
try {
|
||||||
|
conn = connect1();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CompletableFuture.failedFuture(e);
|
||||||
|
}
|
||||||
|
localConnection.set(conn);
|
||||||
|
}
|
||||||
|
return CompletableFuture.completedFuture(conn);
|
||||||
|
} else {
|
||||||
|
return connect0();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected C connect1() {
|
||||||
|
CompletableFuture<C> future = group.createClient(tcp, this.address.randomAddress(), readTimeoutSeconds, writeTimeoutSeconds)
|
||||||
|
.thenApply(c -> (C) createConnection(-2, c).setMaxPipelines(maxPipelines));
|
||||||
|
R virtualReq = createVirtualRequestAfterConnect();
|
||||||
|
if (virtualReq != null) {
|
||||||
|
future = future.thenCompose(conn -> conn.writeVirtualRequest(virtualReq).thenApply(v -> conn));
|
||||||
|
}
|
||||||
|
if (authenticate != null) {
|
||||||
|
future = future.thenCompose(authenticate);
|
||||||
|
}
|
||||||
|
return future.thenApply(c -> (C) c.setAuthenticated(true)).join();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CompletableFuture<C> connect0() {
|
||||||
final int size = this.connArray.length;
|
final int size = this.connArray.length;
|
||||||
WorkThread workThread = WorkThread.currWorkThread();
|
WorkThread workThread = WorkThread.currWorkThread();
|
||||||
final int connIndex = (workThread != null && workThread.threads() == size) ? workThread.index() : (int) Math.abs(connIndexSeq.getAndIncrement()) % size;
|
final int connIndex = (workThread != null && workThread.threads() == size) ? workThread.index() : (int) Math.abs(connIndexSeq.getAndIncrement()) % size;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.*;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.*;
|
import java.util.concurrent.atomic.*;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
|
import org.redkale.annotation.Nullable;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
import org.redkale.util.ByteArray;
|
import org.redkale.util.ByteArray;
|
||||||
|
|
||||||
@@ -29,10 +30,14 @@ import org.redkale.util.ByteArray;
|
|||||||
*/
|
*/
|
||||||
public abstract class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
|
public abstract class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
|
||||||
|
|
||||||
protected final int index; //从0开始, connArray的下坐标
|
//=-1 表示连接放在connAddrEntrys存储
|
||||||
|
//=-2 表示连接放在ThreadLocal存储
|
||||||
|
//>=0 表示connArray的下坐标,从0开始
|
||||||
|
protected final int index;
|
||||||
|
|
||||||
protected final Client client;
|
protected final Client client;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
protected final LongAdder respWaitingCounter; //可能为null
|
protected final LongAdder respWaitingCounter; //可能为null
|
||||||
|
|
||||||
protected final LongAdder doneRequestCounter = new LongAdder();
|
protected final LongAdder doneRequestCounter = new LongAdder();
|
||||||
@@ -47,6 +52,7 @@ public abstract class ClientConnection<R extends ClientRequest, P> implements Co
|
|||||||
|
|
||||||
ClientFuture currHalfWriteFuture; //pauseWriting=true,此字段才会有值; pauseWriting=false,此字段值为null
|
ClientFuture currHalfWriteFuture; //pauseWriting=true,此字段才会有值; pauseWriting=false,此字段值为null
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private final Client.AddressConnEntry connEntry;
|
private final Client.AddressConnEntry connEntry;
|
||||||
|
|
||||||
protected final AsyncConnection channel;
|
protected final AsyncConnection channel;
|
||||||
@@ -70,8 +76,8 @@ public abstract class ClientConnection<R extends ClientRequest, P> implements Co
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
this.codec = createCodec();
|
this.codec = createCodec();
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.connEntry = index >= 0 ? null : client.connAddrEntrys.get(channel.getRemoteAddress());
|
this.connEntry = index == -2 ? null : (index >= 0 ? null : client.connAddrEntrys.get(channel.getRemoteAddress()));
|
||||||
this.respWaitingCounter = index >= 0 ? client.connRespWaitings[index] : this.connEntry.connRespWaiting;
|
this.respWaitingCounter = index == -2 ? new LongAdder() : (index >= 0 ? client.connRespWaitings[index] : this.connEntry.connRespWaiting);
|
||||||
this.channel = channel.beforeCloseListener(this);
|
this.channel = channel.beforeCloseListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +96,21 @@ public abstract class ClientConnection<R extends ClientRequest, P> implements Co
|
|||||||
respFuture.setTimeout(client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS));
|
respFuture.setTimeout(client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
respWaitingCounter.increment(); //放在writeChannelInWriteThread计数会延迟,导致不准确
|
respWaitingCounter.increment(); //放在writeChannelInWriteThread计数会延迟,导致不准确
|
||||||
|
if (client.isThreadLocalConnMode()) {
|
||||||
|
offerRespFuture(respFuture);
|
||||||
|
writeArray.clear();
|
||||||
|
request.writeTo(this, writeArray);
|
||||||
|
doneRequestCounter.increment();
|
||||||
|
if (writeArray.length() > 0) {
|
||||||
|
channel.write(writeArray, this, writeHandler);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (channel.inCurrWriteThread()) {
|
if (channel.inCurrWriteThread()) {
|
||||||
writeChannelInThread(request, respFuture);
|
writeChannelInThread(request, respFuture);
|
||||||
} else {
|
} else {
|
||||||
channel.executeWrite(() -> writeChannelInThread(request, respFuture));
|
channel.executeWrite(() -> writeChannelInThread(request, respFuture));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return respFuture;
|
return respFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.redkale.net.client;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import org.redkale.net.AsyncIOThread;
|
|
||||||
import org.redkale.util.ByteBufferPool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端IO读线程
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 详情见: https://redkale.org
|
|
||||||
*
|
|
||||||
* @author zhangjx
|
|
||||||
*
|
|
||||||
* @since 2.8.0
|
|
||||||
*/
|
|
||||||
public class ClientReadIOThread extends AsyncIOThread {
|
|
||||||
|
|
||||||
public ClientReadIOThread(ThreadGroup g, String name, int index, int threads,
|
|
||||||
ExecutorService workExecutor, ByteBufferPool safeBufferPool) throws IOException {
|
|
||||||
super(g, name, index, threads, workExecutor, safeBufferPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.redkale.net.client;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.CompletionHandler;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import org.redkale.net.AsyncIOThread;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端IO写线程
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 详情见: https://redkale.org
|
|
||||||
*
|
|
||||||
* @author zhangjx
|
|
||||||
*
|
|
||||||
* @since 2.8.0
|
|
||||||
*/
|
|
||||||
public class ClientWriteIOThread extends AsyncIOThread {
|
|
||||||
|
|
||||||
private final AtomicBoolean writingFlag = new AtomicBoolean();
|
|
||||||
|
|
||||||
private final BlockingQueue<ClientFuture> requestQueue = new LinkedBlockingQueue<>();
|
|
||||||
|
|
||||||
public ClientWriteIOThread(ThreadGroup g, String name, int index, int threads,
|
|
||||||
ExecutorService workExecutor, ByteBufferPool safeBufferPool) throws IOException {
|
|
||||||
super(g, name, index, threads, workExecutor, safeBufferPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerRequest(ClientConnection conn, ClientRequest request, ClientFuture respFuture) {
|
|
||||||
requestQueue.offer(respFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendHalfWrite(ClientConnection conn, ClientRequest request, Throwable halfRequestExc) {
|
|
||||||
ClientFuture respFuture = conn.createClientFuture(request);
|
|
||||||
respFuture.resumeHalfRequestFlag = true;
|
|
||||||
if (halfRequestExc != null) { //halfRequestExc不为null时需要把当前halfRequest移除
|
|
||||||
conn.pauseRequests.poll();
|
|
||||||
}
|
|
||||||
requestQueue.offer(respFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final ByteBuffer buffer = getBufferSupplier().get();
|
|
||||||
final int capacity = buffer.capacity();
|
|
||||||
final ByteArray writeArray = new ByteArray();
|
|
||||||
final Map<ClientConnection, List<ClientFuture>> map = new HashMap<>();
|
|
||||||
final ObjectPool<List> listPool = ObjectPool.createUnsafePool(Utility.cpus() * 2, () -> new ArrayList(), null, t -> {
|
|
||||||
t.clear();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
while (!isClosed()) {
|
|
||||||
ClientFuture entry;
|
|
||||||
try {
|
|
||||||
while ((entry = requestQueue.take()) != null) {
|
|
||||||
map.clear();
|
|
||||||
if (entry.resumeHalfRequestFlag != null) { //将暂停的pauseRequests写入list
|
|
||||||
List<ClientFuture> cl = map.computeIfAbsent(entry.conn, c -> listPool.get());
|
|
||||||
for (ClientFuture f : (Collection<ClientFuture>) entry.conn.pauseRequests) {
|
|
||||||
if (!f.isDone()) {
|
|
||||||
entry.conn.offerRespFuture(f);
|
|
||||||
cl.add(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.conn.pauseRequests.clear();
|
|
||||||
entry.conn.pauseWriting.set(false);
|
|
||||||
} else if (!entry.isDone()) {
|
|
||||||
entry.conn.offerRespFuture(entry);
|
|
||||||
if (entry.conn.pauseWriting.get()) {
|
|
||||||
entry.conn.pauseRequests.add(entry);
|
|
||||||
} else {
|
|
||||||
map.computeIfAbsent(entry.conn, c -> listPool.get()).add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ((entry = requestQueue.poll()) != null) {
|
|
||||||
if (entry.resumeHalfRequestFlag != null) { //将暂停的pauseRequests写入list
|
|
||||||
List<ClientFuture> cl = map.computeIfAbsent(entry.conn, c -> listPool.get());
|
|
||||||
for (ClientFuture f : (Collection<ClientFuture>) entry.conn.pauseRequests) {
|
|
||||||
if (!f.isDone()) {
|
|
||||||
entry.conn.offerRespFuture(f);
|
|
||||||
cl.add(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.conn.pauseRequests.clear();
|
|
||||||
entry.conn.pauseWriting.set(false);
|
|
||||||
} else if (!entry.isDone()) {
|
|
||||||
entry.conn.offerRespFuture(entry);
|
|
||||||
if (entry.conn.pauseWriting.get()) {
|
|
||||||
entry.conn.pauseRequests.add(entry);
|
|
||||||
} else {
|
|
||||||
map.computeIfAbsent(entry.conn, c -> listPool.get()).add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map.forEach((conn, list) -> {
|
|
||||||
writeArray.clear();
|
|
||||||
int i = -1;
|
|
||||||
for (ClientFuture en : list) {
|
|
||||||
++i;
|
|
||||||
ClientRequest request = en.request;
|
|
||||||
request.writeTo(conn, writeArray);
|
|
||||||
conn.doneRequestCounter.increment();
|
|
||||||
if (!request.isCompleted()) {
|
|
||||||
conn.pauseWriting.set(true);
|
|
||||||
conn.pauseRequests.addAll(list.subList(i, list.size()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (writeArray.length() > capacity) { //合并的数据包不能太大
|
|
||||||
conn.channel.write(writeArray, conn, writeHandler);
|
|
||||||
writeArray.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listPool.accept(list);
|
|
||||||
//channel.write
|
|
||||||
if (writeArray.length() > 0) {
|
|
||||||
if (writeArray.length() <= capacity) {
|
|
||||||
buffer.clear();
|
|
||||||
buffer.put(writeArray.content(), 0, writeArray.length());
|
|
||||||
buffer.flip();
|
|
||||||
conn.channel.write(buffer, conn, writeHandler);
|
|
||||||
} else {
|
|
||||||
conn.channel.write(writeArray, conn, writeHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final CompletionHandler<Integer, ClientConnection> writeHandler = new CompletionHandler<Integer, ClientConnection>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ClientConnection attachment) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ClientConnection attachment) {
|
|
||||||
attachment.dispose(exc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user