Transport.pollConnection的负载均衡策略改成轮询
This commit is contained in:
@@ -86,6 +86,7 @@
|
|||||||
load: 加载文件,多个用;隔开。
|
load: 加载文件,多个用;隔开。
|
||||||
默认置入的system.property.的有:
|
默认置入的system.property.的有:
|
||||||
System.setProperty("net.transport.pinginterval", "30");
|
System.setProperty("net.transport.pinginterval", "30");
|
||||||
|
System.setProperty("net.transport.checkinterval", "30");
|
||||||
System.setProperty("convert.json.tiny", "true");
|
System.setProperty("convert.json.tiny", "true");
|
||||||
System.setProperty("convert.bson.tiny", "true");
|
System.setProperty("convert.bson.tiny", "true");
|
||||||
System.setProperty("convert.json.pool.size", "128");
|
System.setProperty("convert.json.pool.size", "128");
|
||||||
|
|||||||
@@ -315,7 +315,8 @@ public final class Application {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSecond, writeTimeoutSecond, strategy);
|
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSecond, writeTimeoutSecond, strategy);
|
||||||
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"));
|
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"))
|
||||||
|
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30"));
|
||||||
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
|
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
|
||||||
Thread.currentThread().setContextClassLoader(this.classLoader);
|
Thread.currentThread().setContextClassLoader(this.classLoader);
|
||||||
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
|
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
|
||||||
|
|||||||
@@ -5,13 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.net;
|
package org.redkale.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
@@ -46,6 +47,8 @@ public final class Transport {
|
|||||||
supportTcpNoDelay = tcpNoDelay;
|
supportTcpNoDelay = tcpNoDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final AtomicInteger seq = new AtomicInteger(-1);
|
||||||
|
|
||||||
protected final TransportFactory factory;
|
protected final TransportFactory factory;
|
||||||
|
|
||||||
protected final String name; //即<group>的name属性
|
protected final String name; //即<group>的name属性
|
||||||
@@ -245,29 +248,29 @@ public final class Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//---------------------随机取地址------------------------
|
//---------------------随机取地址------------------------
|
||||||
//从连接池里取
|
int enablecount = 0;
|
||||||
|
final TransportAddress[] newtaddrs = new TransportAddress[taddrs.length];
|
||||||
for (final TransportAddress taddr : taddrs) {
|
for (final TransportAddress taddr : taddrs) {
|
||||||
if (taddr.disabletime > 0) continue;
|
if (taddr.disabletime > 0) continue;
|
||||||
final BlockingQueue<AsyncConnection> queue = taddr.conns;
|
newtaddrs[enablecount++] = taddr;
|
||||||
|
}
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
if (enablecount > 0) { //存在可用的地址
|
||||||
|
final TransportAddress one = newtaddrs[Math.abs(seq.incrementAndGet()) % enablecount];
|
||||||
|
final BlockingQueue<AsyncConnection> queue = one.conns;
|
||||||
if (!queue.isEmpty()) {
|
if (!queue.isEmpty()) {
|
||||||
AsyncConnection conn;
|
AsyncConnection conn;
|
||||||
while ((conn = queue.poll()) != null) {
|
while ((conn = queue.poll()) != null) {
|
||||||
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
|
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
CompletableFuture future = new CompletableFuture();
|
||||||
//从可用/不可用的地址列表中创建连接
|
|
||||||
AtomicInteger count = new AtomicInteger(taddrs.length);
|
|
||||||
CompletableFuture future = new CompletableFuture();
|
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
for (final TransportAddress taddr : taddrs) {
|
|
||||||
if (future.isDone()) return future;
|
|
||||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||||
channel.connect(taddr.address, taddr, new CompletionHandler<Void, TransportAddress>() {
|
channel.connect(one.address, one, new CompletionHandler<Void, TransportAddress>() {
|
||||||
@Override
|
@Override
|
||||||
public void completed(Void result, TransportAddress attachment) {
|
public void completed(Void result, TransportAddress attachment) {
|
||||||
taddr.disabletime = 0;
|
attachment.disabletime = 0;
|
||||||
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSecond, factory.writeTimeoutSecond);
|
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSecond, factory.writeTimeoutSecond);
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||||
@@ -278,23 +281,70 @@ public final class Transport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, TransportAddress attachment) {
|
public void failed(Throwable exc, TransportAddress attachment) {
|
||||||
taddr.disabletime = now;
|
attachment.disabletime = now;
|
||||||
if (count.decrementAndGet() < 1) {
|
|
||||||
future.completeExceptionally(exc);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
channel.close();
|
channel.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
pollConnection0(taddrs, one, now).whenComplete((r, t) -> {
|
||||||
|
if (t != null) {
|
||||||
|
future.completeExceptionally(t);
|
||||||
|
} else {
|
||||||
|
future.complete(r);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
return future;
|
return pollConnection0(taddrs, null, now);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException("transport address = " + addr, ex);
|
throw new RuntimeException("transport address = " + addr, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<AsyncConnection> pollConnection0(TransportAddress[] taddrs, TransportAddress exclude, long now) throws IOException {
|
||||||
|
//从可用/不可用的地址列表中创建连接
|
||||||
|
AtomicInteger count = new AtomicInteger(taddrs.length);
|
||||||
|
CompletableFuture future = new CompletableFuture();
|
||||||
|
for (final TransportAddress taddr : taddrs) {
|
||||||
|
if (taddr == exclude) continue;
|
||||||
|
if (future.isDone()) return future;
|
||||||
|
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||||
|
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||||
|
channel.connect(taddr.address, taddr, new CompletionHandler<Void, TransportAddress>() {
|
||||||
|
@Override
|
||||||
|
public void completed(Void result, TransportAddress attachment) {
|
||||||
|
attachment.disabletime = 0;
|
||||||
|
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSecond, factory.writeTimeoutSecond);
|
||||||
|
if (future.isDone()) {
|
||||||
|
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||||
|
} else {
|
||||||
|
future.complete(asyncConn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable exc, TransportAddress attachment) {
|
||||||
|
attachment.disabletime = now;
|
||||||
|
if (count.decrementAndGet() < 1) {
|
||||||
|
future.completeExceptionally(exc);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
channel.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
|
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
|
||||||
if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return;
|
if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return;
|
||||||
if (!forceClose && conn.isTCP()) {
|
if (!forceClose && conn.isTCP()) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package org.redkale.net;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -22,6 +22,7 @@ import org.redkale.util.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* System.getProperty("net.transport.pinginterval", "30") 心跳周期,默认30秒
|
* System.getProperty("net.transport.pinginterval", "30") 心跳周期,默认30秒
|
||||||
|
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期,默认30秒
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -38,6 +39,8 @@ public class TransportFactory {
|
|||||||
|
|
||||||
public static final String NAME_PINGINTERVAL = "pinginterval";
|
public static final String NAME_PINGINTERVAL = "pinginterval";
|
||||||
|
|
||||||
|
public static final String NAME_CHECKINTERVAL = "checkinterval";
|
||||||
|
|
||||||
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
|
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
|
||||||
|
|
||||||
//传输端的线程池
|
//传输端的线程池
|
||||||
@@ -62,14 +65,17 @@ public class TransportFactory {
|
|||||||
//心跳周期, 单位:秒
|
//心跳周期, 单位:秒
|
||||||
protected int pinginterval;
|
protected int pinginterval;
|
||||||
|
|
||||||
|
//检查不可用地址周期, 单位:秒
|
||||||
|
protected int checkinterval = Integer.getInteger("net.transport.checkinterval", 30);
|
||||||
|
|
||||||
//TCP读取超时秒数
|
//TCP读取超时秒数
|
||||||
protected int readTimeoutSecond;
|
protected int readTimeoutSecond;
|
||||||
|
|
||||||
//TCP写入超时秒数
|
//TCP写入超时秒数
|
||||||
protected int writeTimeoutSecond;
|
protected int writeTimeoutSecond;
|
||||||
|
|
||||||
//ping的定时器
|
//ping和检查的定时器
|
||||||
private ScheduledThreadPoolExecutor pingScheduler;
|
private ScheduledThreadPoolExecutor scheduler;
|
||||||
|
|
||||||
protected SSLContext sslContext;
|
protected SSLContext sslContext;
|
||||||
|
|
||||||
@@ -101,17 +107,23 @@ public class TransportFactory {
|
|||||||
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
|
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
|
||||||
if (conf != null) {
|
if (conf != null) {
|
||||||
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
|
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
|
||||||
|
this.checkinterval = conf.getIntValue(NAME_CHECKINTERVAL, this.checkinterval);
|
||||||
}
|
}
|
||||||
|
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||||
|
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryTask-Thread");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
this.scheduler.scheduleAtFixedRate(() -> {
|
||||||
|
checks();
|
||||||
|
}, checkinterval, checkinterval, TimeUnit.SECONDS);
|
||||||
|
|
||||||
if (this.pinginterval > 0) {
|
if (this.pinginterval > 0) {
|
||||||
if (this.pingScheduler == null && pingBuffer != null) {
|
if (pingBuffer != null) {
|
||||||
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
|
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
|
||||||
this.pongLength = pongLength;
|
this.pongLength = pongLength;
|
||||||
this.pingScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
|
||||||
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryPingTask-Thread");
|
scheduler.scheduleAtFixedRate(() -> {
|
||||||
t.setDaemon(true);
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
pingScheduler.scheduleAtFixedRate(() -> {
|
|
||||||
pings();
|
pings();
|
||||||
}, pinginterval, pinginterval, TimeUnit.SECONDS);
|
}, pinginterval, pinginterval, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
@@ -309,7 +321,7 @@ public class TransportFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void shutdownNow() {
|
public void shutdownNow() {
|
||||||
if (this.pingScheduler != null) this.pingScheduler.shutdownNow();
|
if (this.scheduler != null) this.scheduler.shutdownNow();
|
||||||
try {
|
try {
|
||||||
this.channelGroup.shutdownNow();
|
this.channelGroup.shutdownNow();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -317,8 +329,7 @@ public class TransportFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pings() {
|
private void checks() {
|
||||||
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
|
|
||||||
List<WeakReference> nulllist = new ArrayList<>();
|
List<WeakReference> nulllist = new ArrayList<>();
|
||||||
for (WeakReference<Transport> ref : transportReferences) {
|
for (WeakReference<Transport> ref : transportReferences) {
|
||||||
Transport transport = ref.get();
|
Transport transport = ref.get();
|
||||||
@@ -327,6 +338,36 @@ public class TransportFactory {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Transport.TransportAddress[] taddrs = transport.getTransportAddresses();
|
Transport.TransportAddress[] taddrs = transport.getTransportAddresses();
|
||||||
|
for (final Transport.TransportAddress taddr : taddrs) {
|
||||||
|
if (taddr.disabletime < 1) continue; //可用
|
||||||
|
try {
|
||||||
|
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(transport.group);
|
||||||
|
channel.connect(taddr.address, taddr, new CompletionHandler<Void, Transport.TransportAddress>() {
|
||||||
|
@Override
|
||||||
|
public void completed(Void result, Transport.TransportAddress attachment) {
|
||||||
|
attachment.disabletime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable exc, Transport.TransportAddress attachment) {
|
||||||
|
attachment.disabletime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (WeakReference ref : nulllist) {
|
||||||
|
transportReferences.remove(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pings() {
|
||||||
|
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
|
||||||
|
for (WeakReference<Transport> ref : transportReferences) {
|
||||||
|
Transport transport = ref.get();
|
||||||
|
if (transport == null) continue;
|
||||||
|
Transport.TransportAddress[] taddrs = transport.getTransportAddresses();
|
||||||
for (final Transport.TransportAddress taddr : taddrs) {
|
for (final Transport.TransportAddress taddr : taddrs) {
|
||||||
final BlockingQueue<AsyncConnection> queue = taddr.conns;
|
final BlockingQueue<AsyncConnection> queue = taddr.conns;
|
||||||
AsyncConnection conn;
|
AsyncConnection conn;
|
||||||
@@ -380,9 +421,6 @@ public class TransportFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (WeakReference ref : nulllist) {
|
|
||||||
transportReferences.remove(ref);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkName(String name) { //不能含特殊字符
|
private static boolean checkName(String name) { //不能含特殊字符
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package org.redkale.test.net;
|
|||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
import org.redkale.net.http.HttpServer;
|
import org.redkale.net.http.HttpServer;
|
||||||
import org.redkale.net.sncp.Sncp;
|
import org.redkale.net.sncp.Sncp;
|
||||||
@@ -21,7 +22,7 @@ public class TransportTest {
|
|||||||
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
|
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
|
System.setProperty("net.transport.checkinterval", "2");
|
||||||
List<InetSocketAddress> addrs = new ArrayList<>();
|
List<InetSocketAddress> addrs = new ArrayList<>();
|
||||||
addrs.add(new InetSocketAddress("127.0.0.1", 22001));
|
addrs.add(new InetSocketAddress("127.0.0.1", 22001));
|
||||||
addrs.add(new InetSocketAddress("127.0.0.1", 22002));
|
addrs.add(new InetSocketAddress("127.0.0.1", 22002));
|
||||||
@@ -42,13 +43,27 @@ public class TransportTest {
|
|||||||
Transport transport = factory.createTransportTCP("", null, addrs);
|
Transport transport = factory.createTransportTCP("", null, addrs);
|
||||||
System.out.println(String.format(format, System.currentTimeMillis()));
|
System.out.println(String.format(format, System.currentTimeMillis()));
|
||||||
try {
|
try {
|
||||||
AsyncConnection firstconn = transport.pollConnection(null).join();
|
CountDownLatch cdl = new CountDownLatch(20);
|
||||||
System.out.println(firstconn);
|
for (int i = 0; i < 20; i++) {
|
||||||
if (firstconn != null) transport.offerConnection(false, firstconn);
|
transport.pollConnection(null).whenComplete((r, t) -> {
|
||||||
AsyncConnection conn = transport.pollConnection(null).join();
|
cdl.countDown();
|
||||||
System.out.println(conn + "-------应该与前值相同");
|
System.out.println("连接: " + r.getRemoteAddress());
|
||||||
conn = transport.pollConnection(null).join();
|
});
|
||||||
System.out.println(conn + "-------应该与前值不同");
|
}
|
||||||
|
cdl.await();
|
||||||
|
HttpServer server = new HttpServer();
|
||||||
|
DefaultAnyValue servconf = DefaultAnyValue.create("port", 22005);
|
||||||
|
server.init(servconf);
|
||||||
|
server.start();
|
||||||
|
Thread.sleep(4000);
|
||||||
|
CountDownLatch cdl2 = new CountDownLatch(20);
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
transport.pollConnection(null).whenComplete((r, t) -> {
|
||||||
|
cdl2.countDown();
|
||||||
|
System.out.println("连接: " + r.getRemoteAddress());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cdl2.await();
|
||||||
} finally {
|
} finally {
|
||||||
System.out.println(String.format(format, System.currentTimeMillis()));
|
System.out.println(String.format(format, System.currentTimeMillis()));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user