I saw the following description in the official tomcat configuration documentation (APR connector description omitted):
Java Blocking Connector Java Nio Blocking Connector
Classname Http11Protocol Http11NioProtocol
Tomcat Version 3.x 4.x 5.x 6.x 6.x
Support Polling NO YES
Polling Size N/A Unlimited - Restricted by mem
Read HTTP Request Blocking Non Blocking
Read HTTP Body Blocking Sim Blocking
Write HTTP Response Blocking Sim Blocking
SSL Support Java SSL Java SSL
SSL Handshake Blocking Non blocking
Max Connections maxThreads See polling size
What does "Sim Blocking" mean?
According to Filip Hanik, a Tomcat committer, it means "simulated blocking". (Reference: Tomcat User Mailing list post)
Just a guess, but it could stand for simulated blocking, meaning a blocking api wrapped around the underlying non-blocking nio api.
SourceCode: https://github.com/apache/tomcat/blob/8.5.x/java/org/apache/tomcat/util/net/NioEndpoint.java
/**
* NioEndpoint.NioSocketWrapper.fillReadBuffer() 用于直接读取内容到 传入的任意 ByteBuffer 中
*
* #param block 是否阻塞读
* #param buffer 待接收数据的buffer
* #return 读取到的字节数
* #throws IOException
*/
private int fillReadBuffer(boolean block, ByteBuffer buffer) throws IOException {
int n = 0;
if (getSocket() == NioChannel.CLOSED_NIO_CHANNEL) {
throw new ClosedChannelException();
}
if (block) { // if no readListener. block variable is true.
long timeout = getReadTimeout();
long startNanos = 0;
do {
if (startNanos > 0) {
long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
if (elapsedMillis == 0) {
elapsedMillis = 1;
}
timeout -= elapsedMillis;
if (timeout <= 0) {
throw new SocketTimeoutException();
}
}
n = getSocket().read(buffer);
if (n == -1) {
throw new EOFException();
} else if (n == 0) {
if (!readBlocking) {
readBlocking = true;
registerReadInterest(); // <=============== (向关联的Poller)注册读事件.
}
synchronized (readLock) {
if (readBlocking) {
try {
if (timeout > 0) {
startNanos = System.nanoTime();
readLock.wait(timeout); // block itself here < ===================
} else {
readLock.wait();
}
} catch (InterruptedException e) {
// Continue
}
}
}
}
} while (n == 0); // TLS needs to loop as reading zero application bytes is possible
} else {
n = getSocket().read(buffer);
if (n == -1) {
throw new EOFException();
}
}
return n;
}
Related
1.scene description:
Device sen data to Netty Server(about ## 20ms intervals ##), Netty Server forward msg to Client ## Immediately ##(IOS or Android).
2.Associated business code
ctx.writeAndFlush(msg)
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
int writeSpinCount = -1;
boolean setOpWrite = false;
for (;;) {
//Get the data of the first node that needs to be flushed
Object msg = in.current();
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
boolean done = false;
long flushedAmount = 0;
// Get the number of spin lock iterations
if (writeSpinCount == -1) {
writeSpinCount = config().getWriteSpinCount();
}
// Spin, write out the current node
for (int i = writeSpinCount - 1; i >= 0; i --) {
int localFlushedAmount = doWriteBytes(buf);
if (localFlushedAmount == 0) {
setOpWrite = true;
break;
}
flushedAmount += localFlushedAmount;
if (!buf.isReadable()) {
done = true;
break;
}
}
in.progress(flushedAmount);
// After writing, delete the current node
if (done) {
in.remove();
} else {
break;
}
}
}
}
protected int doWriteBytes(ByteBuf buf) throws Exception {
final int expectedWrittenBytes = buf.readableBytes();
return buf.readBytes(javaChannel(), expectedWrittenBytes);
}
3.issue
The netty Server can receive the device data in time
and netty Server can write the data to Socket Buffer in time also.
But The Netty client receives the message delay!!!(eg. 5s delay)
4.Server bandwidth configuration
Inbound 100M/bps bit per seconds.
Outbound 5M/bps bit per seconds.
Client terminal length packer caused by the problem.
I have to make a function call. If it fails, I retry another 2 times. If it still fails after the 2 retrys, I throw an exception.
Below is my current code which is working:
for (int retry = 0; retry < 4; retry++) {
try {
callFunction();
break;
} catch (Exception e) {
if (retry >= 0 && retry < 3) {
warn("callFunction failed. Retrying.." +(retry+1));
} else {
warn("Retried maximum number of "+(retry+1)+" times. Failing the script");
e.printStackTrace();
}
}
}
OUTPUT:
callFunction failed. Retrying..1
callFunction failed. Retrying..2
callFunction failed. Retrying..3
Retried maximum number of 4 times. Failing the script
I understand that this is not the most efficient way to code even though it is working. Can you help me refactor this code to meet the best practice standards of Java clean code?
What's not so great here:
The deep nesting
The redundant retry >= 0 condition
The repeated (retry + 1)
The repeated used of magic number 4
I think a while loop might flow more naturally here,
and checking at the end the retry count.
int maxRetries = 3;
int retry = 0;
Exception thrown = null;
while (retry < maxRetries) {
try {
callFunction();
break;
} catch (Exception e) {
thrown = e;
retry++;
warn("callFunction failed. Retrying.." + retry);
}
}
if (retry == maxRetries) {
warn("reached max");
thrown.printStackTrace();
}
Actually, it's even better if you turn this into a function.
There will be fewer variables:
void executeWithRetries(int maxRetries) {
Exception thrown = null;
for (int retry = 0; retry < maxRetries; retry++) {
try {
callFunction();
return;
} catch (Exception e) {
thrown = e;
warn("callFunction failed. Retrying.." + retry);
}
}
warn("reached max");
thrown.printStackTrace();
}
Another alternative is to use a library for something like this. Using Failsafe:
RetryPolicy retryPolicy = new RetryPolicy().withMaxRetries(3);
Failsafe.with(retryPolicy)
.onRetry((c, f, ctx) -> warn("callFunction failed. Retrying.." + ctx.getExecutions()))
.onFailure(e -> {
warn("Retried maximum number of times. Failing the script");
e.printStackTrace();
});
.run(this::callFunction);
I am looking at memory allocations of an application that writes to sockets, and I see that a lot of allocations are done in SocketOutputStream.socketWrite in InetAddress:
Is there a way do get rid of these allocations?
I am profiling on Windows using Java 1.8.0_40.
Below is the source code of SocketOutputStream from jdk 1.8, I do not see the code that can allocate these objects
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
int len) throws IOException;
/**
* Writes to the socket with appropriate locking of the
* FileDescriptor.
* #param b the data to be written
* #param off the start offset in the data
* #param len the number of bytes that are written
* #exception IOException If an I/O error has occurred.
*/
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || off + len > b.length) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException();
}
FileDescriptor fd = impl.acquireFD();
try {
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (se instanceof sun.net.ConnectionResetException) {
impl.setConnectionResetPending();
se = new SocketException("Connection reset");
}
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}
As stated by Google at Android Developers
public int bulkTransfer (UsbEndpoint endpoint, byte[] buffer, int
length, int timeout)
Returns length of data transferred (or zero) for success, or negative value for failure
Well, after performing a bulkTransfer in my app for a USB CDC communication
// send data to usb device
byte[] bytes = data.getBytes();
sentBytes = connection.bulkTransfer(output, bytes, bytes.length, 1000);
sentBytes is receiving the value of 16, which is great, because I'm sending 16 bytes indeed.
It should indicate that the bytes were sent correctly. :-)
However, they're NOT. I can't see any results on the other side.
After doing a rearch I found out that it could be an Interface problem, so here it's how I'm getting it from the device:
private void setupConnection()
{
// find the right interface
for(int i = 0; i < usbDevice.getInterfaceCount(); i++)
{
// communications device class (CDC) type device
if(usbDevice.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
{
intf = usbDevice.getInterface(i);
// find the endpoints
for(int j = 0; j < intf.getEndpointCount(); j++)
{
if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
{
// from android to device
output = intf.getEndpoint(j);
}
if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_IN && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
{
// from device to android
input = intf.getEndpoint(j);
}
}
}
}
}
What could I be possibly missing?
Following is the source code:
//public class ConnectionPool implements Runnable
public class ConnectionPool {
private static Logger loger_error = Logger.getLogger("error");
// JDBC Driver name
String driverName;
// JDBC Connection URL
String connectionURL;
// Minimum size of the pool
int connectionPoolSize;
// Maximum size of the pool
int connectionPoolMax;
// Maximum number of uses for a single connection, or -1 for none
int connectionUseCount;
// Maximum connection idle time (in minutes)
int connectionTimeout;
// Additional JDBC properties
String userName;
String password;
// The Connection pool. This is a vector of ConnectionObject
// objects
Vector pool;
// The maximum number of simultaneous connections as reported
// by the JDBC driver
int maxConnections = -1;
// Scheduler scheduler;
// Timeout value
public static int TIMEOUT_MS = 20000;
/**
* Initializes the ConnectionPool object using 'ConnectionPool.cfg' as the
* configuration file
*
* #return true if the ConnectionPool was initialized properly
*/
/*
* public boolean initialize() throws Exception { return
* initialize("com/omh/jdbc/ConnectionPool.cfg"); }
*/
/**
* Initializes the ConnectionPool object with the specified configuration
* file
*
* #param config
* Configuration file name
* #return true if the ConnectionPool was initialized properly
*/
public void initialize(String driverName, String connectionURL,
int connectionPoolSize, int connectionPoolMax,
int connectionUseCount, int connectionTimeout, String userName,
String password) throws Exception {
this.driverName = driverName;
this.connectionURL = connectionURL;
this.connectionPoolSize = connectionPoolSize;
this.connectionPoolMax = connectionPoolMax;
this.connectionUseCount = connectionUseCount;
this.connectionTimeout = connectionTimeout;
this.userName = userName;
this.password = password;
createPool();
// scheduler = new Scheduler();
// scheduler.schedule(this, TIMEOUT_MS);
}
/**
* Destroys the pool and it's contents. Closes any open JDBC connections and
* frees all resources
*/
public void destroy() {
try {
// Clear our pool
if (pool != null) {
// Loop throught the pool and close each connection
for (int i = 0; i < pool.size(); i++) {
((MangoDBConnection) pool.elementAt(i)).closeConnection();
}
}
pool = null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Gets an available JDBC Connection. Connections will be created if
* necessary, up to the maximum number of connections as specified in the
* configuration file.
*
* #return JDBC Connection, or null if the maximum number of connections has
* been exceeded
*/
public synchronized MangoDBConnection getConnection() {
// If there is no pool it must have been destroyed
if (pool == null) {
return null;
}
MangoDBConnection connectionObject = null;
int poolSize = pool.size();
// Get the next available connection
for (int i = 0; i < poolSize; i++) {
// Get the ConnectionObject from the pool
MangoDBConnection co = (MangoDBConnection) pool.elementAt(i);
// If this is a valid connection and it is not in use,
// grab it
if (co.isAvailable()) {
connectionObject = co;
break;
}
}
// No more available connections. If we aren't at the
// maximum number of connections, create a new entry
// in the pool
if (connectionObject == null) {
if ((connectionPoolMax < 0)
|| ((connectionPoolMax > 0) && (poolSize < connectionPoolMax))) {
// Add a new connection.
int i = addConnection();
// If a new connection was created, use it
if (i >= 0) {
connectionObject = (MangoDBConnection) pool.elementAt(i);
}
} else {
LogManager.log("Maximum number of connections exceeded");
loger_error.error("Maximum number of connections exceeded");
}
}
// If we have a connection, set the last time accessed,
// the use count, and the in use flag
if (connectionObject != null) {
connectionObject.use();
connectionObject.touch();
}
return connectionObject;
}
/**
* Places the connection back into the connection pool, or closes the
* connection if the maximum use count has been reached
*
* #param Connection
* object to close
*/
public synchronized void releaseConnection(MangoDBConnection con) {
removeFromPool(con);
}
public synchronized void release(MangoDBConnection con) {
if ((connectionUseCount > 0)
&& (con.getUseCount() >= connectionUseCount)) {
removeFromPool(con);
// add new connection upon releasing one
addConnection();
} else {
con.touch();
con.free();
}
/*
* // Find the connection in the pool int index = find(con);
* System.out.println("close"); if (index != -1) { ConnectionObject co =
* (ConnectionObject) pool.elementAt(index);
* // If the use count exceeds the max, remove it from // the pool. if
* ((connectionUseCount > 0) && (co.useCount >= connectionUseCount)) {
* trace("Connection use count exceeded"); removeFromPool(index); } else { //
* Clear the use count and reset the time last used co.touch();
* co.free(); } }
*/
}
/**
* Prints the contents of the connection pool to the standard output device
*/
public void printPool() {
printPool(new PrintWriter(System.out));
}
/**
* Prints the contents of the connection pool to the given PrintWriter
*/
public void printPool(PrintWriter out) {
out.println("--ConnectionPool--");
if (pool != null) {
for (int i = 0; i < pool.size(); i++) {
MangoDBConnection co = (MangoDBConnection) pool.elementAt(i);
out.println("" + i + "=" + co);
}
}
}
/**
* Returns an enumeration of the ConnectionObject objects that represent the
* pool
*/
public Enumeration getConnectionPoolObjects() {
return pool.elements();
}
public int returnConnectionCount() {
return connectionUseCount;
}
public int returnMaxPoolSize() {
return connectionPoolMax;
}
public int returnInitPoolSize() {
return connectionPoolSize;
}
/**
* Removes the ConnectionObject from the pool at the given index
*
* #param index
* Index into the pool vector
*/
private synchronized void removeFromPool(MangoDBConnection con) {
// Make sure the pool and index are valid
if (pool != null) {
con.closeConnection();
pool.removeElement(con);
}
}
/**
* Creates the initial connection pool. A timer thread is also created so
* that connection timeouts can be handled.
*
* #return true if the pool was created
*/
private void createPool() throws Exception {
// Dump the parameters we are going to use for the pool.
// We don't know what type of servlet environment we will
// be running in - this may go to the console or it
// may be redirected to a log file
LogManager.log("JDBCDriver = " + driverName);
LogManager.log("JDBCConnectionURL = " + connectionURL);
LogManager.log("ConnectionPoolSize = " + connectionPoolSize);
LogManager.log("ConnectionPoolMax = " + connectionPoolMax);
LogManager.log("ConnectionUseCount = " + connectionUseCount);
LogManager.log("ConnectionTimeout = " + connectionTimeout + " seconds");
LogManager.log("Registering " + driverName);
Driver d = (Driver) Class.forName(driverName).newInstance();
// Create the vector for the pool
pool = new Vector();
// Bring the pool to the minimum size
fillPool(connectionPoolSize);
}
/**
* Adds a new connection to the pool
*
* #return Index of the new pool entry, or -1 if an error has occurred
*/
public int addConnection() {
int index = -1;
try {
// Calculate the new size of the pool
int size = pool.size() + 1;
// Create a new entry
fillPool(size);
// Set the index pointer to the new connection if one
// was created
if (size == pool.size()) {
index = size - 1;
}
} catch (Exception ex) {
System.out.println("SSSSSSS");
ex.printStackTrace();
}
return index;
}
/**
* Brings the pool to the given size
*/
private synchronized void fillPool(int size) throws Exception {
String userID = this.userName;
String password = this.password;
// userID = getPropertyIgnoreCase(JDBCProperties, "user");
// password = getPropertyIgnoreCase(JDBCProperties, "password");
// Loop while we need to create more connections
while (pool.size() < size) {
MangoDBConnection co = new MangoDBConnectionMSSQL();
// Create the connection
co.makeConnection(connectionURL, userID, password);
// Do some sanity checking on the first connection in
// the pool
if (pool.size() == 0) {
// Get the maximum number of simultaneous connections
// as reported by the JDBC driver
maxConnections = co.getMaxConnections();
}
// Give a warning if the size of the pool will exceed
// the maximum number of connections allowed by the
// JDBC driver
if ((maxConnections > 0) && (size > maxConnections)) {
LogManager
.log("WARNING: Size of pool will exceed safe maximum of "
+ maxConnections);
}
// Clear the in use flag
co.free();
// Set the last access time
co.touch();
pool.addElement(co);
}
} // fillPool()
/**
* Gets a the named propery, ignoring case. Returns null if not found
*
* #param p
* The property set
* #param name
* The property name
* #return The value of the propery, or null if not found
*/
private String getPropertyIgnoreCase(Properties p, String name) {
if ((p == null) || (name == null))
return null;
String value = null;
// Get an enumeration of the property names
Enumeration enumeration = p.propertyNames();
// Loop through the enum, looking for the given property name
while (enumeration.hasMoreElements()) {
String pName = (String) enumeration.nextElement();
if (pName.equalsIgnoreCase(name)) {
value = p.getProperty(pName);
break;
}
}
return value;
}
/**
* Called by the timer each time a clock cycle expires. This gives us the
* opportunity to timeout connections
*/
/*
* public synchronized void run() { // No pool means no work if (pool ==
* null) { return; }
* // Get the current time in milliseconds long now =
* System.currentTimeMillis();
* // Check for any expired connections and remove them for (int i =
* pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject)
* pool.elementAt(i);
* // If the connection is not in use and it has not been // used recently,
* remove it if (!co.inUse) { if ((connectionTimeout > 0) && (co.lastAccess +
* (connectionTimeout * 1000) < now)) { removeFromPool(i); } } }
* // Remove any connections that are no longer open for (int i =
* pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject)
* pool.elementAt(i); try { // If the connection is closed, remove it from
* the pool if (co.con.isClosed()) { trace("Connection closed
* unexpectedly"); removeFromPool(i); } } catch (Exception ex) { } }
* // Now ensure that the pool is still at it's minimum size try { if (pool !=
* null) { if (pool.size() < connectionPoolSize) {
* fillPool(connectionPoolSize); } } } catch (Exception ex) {
* ex.printStackTrace(); }
* // Reschedule ourselves scheduler.schedule(this, TIMEOUT_MS); }
*/
}
Anyone have good idea ?
How to implement connection pooling?
A common cause for leakage of resources in a resource pool (like your connection pool) is that some client of the pool is failing to release the resources under some circumstances. Here's an example:
Resource resource = pool.getResource();
...
// do stuff
...
pool.releaseResource(resource);
This will leak resources if an exception is thrown in the "do stuff" section and allowed to propagate. A non-leaky version of the above is:
Resource resource = pool.getResource();
try {
...
// do stuff
...
} finally {
pool.releaseResource(resource);
}
EDIT: As #Adamski points out, there is no "magic bullet" solution that will solve this kind of problem. The best that I can suggest is to do the following:
Search through your codebase to find all places where a resource is requested from the pool. Then starting at each point, check for leaks and fix; e.g. based on the above pattern.
Create a test suite that exercise all of your request types and run it repeatedly against your service
One more thing. Don't be tempted to try to "fix" the problem by using a finalizer to deal with lost resources. That may make the problem seem to go away, only to reappear later when your system is heavily loaded, or Someone Important is watching you do a demo.
Just as suggestion database pool management can be tricky, if you have further needs like adding connection timeout or adding pool management strategy (may be the case if you have several several thread using the same connection).
So I would suggest to use open source solution such as the Apache DHCP or other open source solution. You will still have to properly close your connections (as Suggested by Stephen C) but it will be give you more flexibility, if you need to implement more complex stuff.
I would store the point in time (and the requesting thread?), when a connection is borrowed in getConnection(), and add a thread to remove them from the pool after some minutes. This could help you to find the culprit :)
You shouldn't close the connection, except there won't be any long going task. I would simply log this.