Java 将数据库连接与Netty 4事件执行器的线程关联
我正在开发一个Netty服务器,可用作Android应用程序的后端。在我当前的实现中,通过特殊的Netty threadpool(而不是I/O线程)通过每个Netty通道使用一个DB连接,在逻辑处理程序中实现对DB的访问,如下所示: 初始化:Java 将数据库连接与Netty 4事件执行器的线程关联,java,database,multithreading,netty,Java,Database,Multithreading,Netty,我正在开发一个Netty服务器,可用作Android应用程序的后端。在我当前的实现中,通过特殊的Netty threadpool(而不是I/O线程)通过每个Netty通道使用一个DB连接,在逻辑处理程序中实现对DB的访问,如下所示: 初始化: EventExecutorGroup logicExecutor = new DefaultEventExecutorGroup(4); EventLoopGroup acceptGroup = new NioEventLoopGroup(); Event
EventExecutorGroup logicExecutor = new DefaultEventExecutorGroup(4);
EventLoopGroup acceptGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{
ServerBootstrap b = new ServerBootstrap();
b.group(acceptGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 50)
.childOption(ChannelOption.SO_KEEPALIVE, false)
.childHandler(new ChannelInitializer<SocketChannel>()
{
@Override
public void initChannel(SocketChannel ch) throws Exception
{
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new IdleStateHandler(5*60, 0, 0));
pipeline.addLast(new ProtobufDelimitedFrameDecoder(65536));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufDecoder(NetMsg.ClientMsg.getDefaultInstance()));
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(logicExecutor, "logic", new ChannelLogicHandler());
}
});
…并关闭通道上的连接处于非活动状态
但据我所知,Netty将其自身线程池中的每个线程与其整个生命周期中的通道相关联,因此在我的例子中,将DefaultEventExecutorGroup(4)用于逻辑处理程序意味着所有通道将由四个线程提供服务,并且对于任何给定的通道,将仅使用线程池中的一个线程。因此,每个执行器线程维护一个DB连接就足以提供数据完整性,而无需任何锁(具有适当的事务隔离级别)。
所以我的问题是,是否可以在线程池中每个线程关联一个DB连接,这样每个连接都是在线程启动时建立的(或者在第一个通道与之关联时),以及如何实现它?如果您希望每个线程都有一个变量,可以使用ThreadLocal字段 大概是这样的:
private static final ThreadLocal<DatabaseConnection> databaseConnection =
new ThreadLocal<DatabaseConnection>() {
@Override protected DatabaseConnection initialValue() {
return DriverManager.getConnection(dbConnectParams[0], dbConnectParams[1], dbConnectParams[2]);
}
};
专用静态最终线程本地数据库连接=
新ThreadLocal(){
@重写受保护的数据库连接初始值(){
返回DriverManager.getConnection(dbConnectParams[0],dbConnectParams[1],dbConnectParams[2]);
}
};
这样,每个线程将有一个不同的数据库连接。当第一次调用get方法时,线程的连接将通过调用创建数据库连接的
initialValue()
方法初始化。后续调用将返回相同的先前值,但您也可以手动为此字段设置新值。我猜我自己找到了解决方案-每个ChannelHandlerContext(ctx)都有自己的EventExecutor,本质上就是线程。因此,我使用hashmap将数据库连接与执行器关联起来。代码:
//Declare hashmap in main server class
private final HashMap<EventExecutor,java.sql.Connection> execConsMap = new HashMap<>(4);
数据库连接在服务器停止时关闭:
logicExecutor.shutdownGracefully().addListener(new GenericFutureListener()
{
@Override
public void operationComplete(Future future) throws Exception
{
for (java.sql.Connection conn : execConsMap.values())
{
try
{
if (!conn.getAutoCommit())
conn.rollback();
} catch (SQLException e)
{ e.printStackTrace(); }
try
{
conn.close();
} catch (SQLException e)
{ e.printStackTrace(); }
}
}
});
谢谢,但在等待可能的答案时,我找到了有效的解决方案,请参见下文:)
public class ChannelLogicHandler extends ChannelInboundHandlerAdapter
{
private java.sql.Connection dbConnection = null; //DB connection saved as private member of logic handler
//........................
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
{
EventExecutor ex = ctx.executor(); //Get channel executor
synchronized(execConsMap)
{
if (execConsMap.containsKey(ex)) //If already processed get DB connection from hashmap
{
dbConnection = execConsMap.get(ex);
}
else //Else create new connection and save in hashmap
{
java.sql.Connection dbc = DriverManager.getConnection(dbConnectParams[0], dbConnectParams[1], dbConnectParams[2]);
if (dbc != null)
{
execConsMap.put(ex, dbc);
dbConnection = dbc;
}
else
{
throw new SQLException("Connection to database failed");
}
}
}
System.out.println("New client connected");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception
{
try
{
if (!dbConnection.getAutoCommit())
{
dbConnection.rollback();
dbConnection.setAutoCommit(true);
}
}
catch (SQLException e)
{
e.printStackTrace();
}
System.out.println("Client disconnected");
super.channelInactive(ctx);
}
logicExecutor.shutdownGracefully().addListener(new GenericFutureListener()
{
@Override
public void operationComplete(Future future) throws Exception
{
for (java.sql.Connection conn : execConsMap.values())
{
try
{
if (!conn.getAutoCommit())
conn.rollback();
} catch (SQLException e)
{ e.printStackTrace(); }
try
{
conn.close();
} catch (SQLException e)
{ e.printStackTrace(); }
}
}
});