Remove bottleneck while create InetSocketAddress in native transport

Motivation:

Everytime a new connection is accepted via EpollSocketServerChannel it will create a new EpollSocketChannel that needs to get the remote and local addresses in the constructor. The current implementation uses new InetSocketAddress(String, int) to create these. This is quite slow due the implementation in oracle and openjdk.

Modifications:

Encode all needed informations into a byte array before return from jni layer and then use new InetSocketAddress(InetAddress, int) to create the socket addresses. This allows to create the InetAddress via a byte[] and so reduce the overhead, this is done either by using InetAddress.getByteAddress(byte[]) or by Inet6Address.getByteAddress(String, byte[], int).

Result:

Reduce performance overhead while accept new connections with native transport
This commit is contained in:
Norman Maurer 2014-10-22 15:25:30 +02:00 committed by Norman Maurer
parent dfd0cc5ea2
commit 73c0f85d63
4 changed files with 173 additions and 8 deletions

View File

@ -173,6 +173,68 @@ jobject createInetSocketAddress(JNIEnv * env, struct sockaddr_storage addr) {
return socketAddr;
}
jbyteArray createInetSocketAddressArray(JNIEnv * env, struct sockaddr_storage addr) {
int port;
if (addr.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
port = ntohs(s->sin_port);
// Encode address and port into the array
unsigned char a[8];
a[0] = s->sin_addr.s_addr >> 24;
a[1] = s->sin_addr.s_addr >> 16;
a[2] = s->sin_addr.s_addr >> 8;
a[3] = s->sin_addr.s_addr;
a[4] = port >> 24;
a[5] = port >> 16;
a[6] = port >> 8;
a[7] = port;
jbyteArray bArray = (*env)->NewByteArray(env, 8);
(*env)->SetByteArrayRegion(env, bArray, 0, 8, (jbyte*) &a);
return bArray;
} else {
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
port = ntohs(s->sin6_port);
if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00
&& s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00
&& s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) {
// IPv4-mapped-on-IPv6
// Encode port into the array and write it into the jbyteArray
unsigned char a[4];
a[0] = port >> 24;
a[1] = port >> 16;
a[2] = port >> 8;
a[3] = port;
jbyteArray bArray = (*env)->NewByteArray(env, 8);
// we only need the last 4 bytes for mapped address
(*env)->SetByteArrayRegion(env, bArray, 0, 4, (jbyte*) &(s->sin6_addr.s6_addr[12]));
(*env)->SetByteArrayRegion(env, bArray, 4, 4, (jbyte*) &a);
return bArray;
} else {
// Encode scopeid and port into the array
unsigned char a[8];
a[0] = s->sin6_scope_id >> 24;
a[1] = s->sin6_scope_id >> 16;
a[2] = s->sin6_scope_id >> 8;
a[3] = s->sin6_scope_id;
a[4] = port >> 24;
a[5] = port >> 16;
a[6] = port >> 8;
a[7] = port;
jbyteArray bArray = (*env)->NewByteArray(env, 24);
(*env)->SetByteArrayRegion(env, bArray, 0, 16, (jbyte*) &(s->sin6_addr.s6_addr));
(*env)->SetByteArrayRegion(env, bArray, 16, 8, (jbyte*) &a);
return bArray;
}
}
}
jobject createDatagramSocketAddress(JNIEnv * env, struct sockaddr_storage addr, int len) {
char ipstr[INET6_ADDRSTRLEN];
int port;
@ -1138,7 +1200,7 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env
return res;
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_remoteAddress(JNIEnv * env, jclass clazz, jint fd) {
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd) {
socklen_t len;
struct sockaddr_storage addr;
@ -1146,10 +1208,10 @@ JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_remoteAddress(JNIEn
if (getpeername(fd, (struct sockaddr*)&addr, &len) == -1) {
return NULL;
}
return createInetSocketAddress(env, addr);
return createInetSocketAddressArray(env, addr);
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_localAddress(JNIEnv * env, jclass clazz, jint fd) {
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd) {
socklen_t len;
struct sockaddr_storage addr;
@ -1157,7 +1219,7 @@ JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_localAddress(JNIEnv
if (getsockname(fd, (struct sockaddr*)&addr, &len) == -1) {
return NULL;
}
return createInetSocketAddress(env, addr);
return createInetSocketAddressArray(env, addr);
}
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval) {

View File

@ -70,8 +70,8 @@ jboolean Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz,
jboolean Java_io_netty_channel_epoll_Native_finishConnect(JNIEnv * env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_accept(JNIEnv * env, jclass clazz, jint fd);
jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len);
jobject Java_io_netty_channel_epoll_Native_remoteAddress(JNIEnv * env, jclass clazz, jint fd);
jobject Java_io_netty_channel_epoll_Native_localAddress(JNIEnv * env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd);
void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv * env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv *env, jclass clazz, jint fd, jint optval);

View File

@ -26,6 +26,7 @@ import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Locale;
@ -200,8 +201,61 @@ final class Native {
public static native boolean connect(int fd, byte[] address, int scopeId, int port) throws IOException;
public static native boolean finishConnect(int fd) throws IOException;
public static native InetSocketAddress remoteAddress(int fd);
public static native InetSocketAddress localAddress(int fd);
public static InetSocketAddress remoteAddress(int fd) {
byte[] addr = remoteAddress0(fd);
return address(addr);
}
public static InetSocketAddress localAddress(int fd) {
byte[] addr = localAddress0(fd);
return address(addr);
}
static InetSocketAddress address(byte[] addr) {
int len = addr.length;
// The last 4 bytes are always the port
final int port = decodeInt(addr, len - 4);
final InetAddress address;
try {
switch (len) {
// 8 bytes:
// - 4 == ipaddress
// - 4 == port
case 8:
byte[] ipv4 = new byte[4];
System.arraycopy(addr, 0, ipv4, 0, 4);
address = InetAddress.getByAddress(ipv4);
break;
// 24 bytes:
// - 16 == ipaddress
// - 4 == scopeId
// - 4 == port
case 24:
byte[] ipv6 = new byte[16];
System.arraycopy(addr, 0, ipv6, 0, 16);
int scopeId = decodeInt(addr, len - 8);
address = Inet6Address.getByAddress(null, ipv6, scopeId);
break;
default:
throw new Error();
}
return new InetSocketAddress(address, port);
} catch (UnknownHostException e) {
throw new Error("Should never happen", e);
}
}
private static int decodeInt(byte[] addr, int index) {
return (addr[index] & 0xff) << 24 |
(addr[index + 1] & 0xff) << 16 |
(addr[index + 2] & 0xff) << 8 |
addr[index + 3] & 0xff;
}
private static native byte[] remoteAddress0(int fd);
private static native byte[] localAddress0(int fd);
public static native int accept(int fd) throws IOException;
public static native void shutdown(int fd, boolean read, boolean write) throws IOException;

View File

@ -0,0 +1,49 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.epoll;
import io.netty.util.NetUtil;
import org.junit.Assert;
import org.junit.Test;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
public class NativeTest {
@Test
public void testAddressIpv4() throws Exception {
InetSocketAddress inetAddress = new InetSocketAddress(NetUtil.LOCALHOST4, 9999);
byte[] bytes = new byte[8];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.put(inetAddress.getAddress().getAddress());
buffer.putInt(inetAddress.getPort());
Assert.assertEquals(inetAddress, Native.address(buffer.array()));
}
@Test
public void testAddressIpv6() throws Exception {
Inet6Address address = NetUtil.LOCALHOST6;
InetSocketAddress inetAddress = new InetSocketAddress(address, 9999);
byte[] bytes = new byte[24];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.put(address.getAddress());
buffer.putInt(address.getScopeId());
buffer.putInt(inetAddress.getPort());
Assert.assertEquals(inetAddress, Native.address(buffer.array()));
}
}