最近在做一个项目,网址见:http://git.oschina.net/qiangzigege/MySQL-Binlog
要解决的一个问题就是:假如我想发起一个连接去连接mysql,成功后,如何让这个连接比较顺畅的纳入到netty的管理范围呢?
---不多说,上代码!
1)NettyQueue.java---存放连接队列
public class NettyQueue {
// poll: 若队列为空,返回null。
// remove:若队列为空,抛出NoSuchElementException异常。
// take:若队列为空,发生阻塞,等待有元素。
// put---无空间会等待
// add--- 满时,立即返回,会抛出异常
// offer---满时,立即返回,不抛异常
// private static final Logger logger =
// LoggerFactory.getLogger(MonitorQueue.class);
private static final Logger logger = LogManager.getLogger(NettyQueue.class);
public static BlockingQueue<Connection> objectQueue = new LinkedBlockingQueue<Connection>();
public static void addObject(Connection obj) {
objectQueue.offer(obj);
new Thread(new Runnable() {// 启动一个线程去触发...
@Override
public void run() {
// TODO Auto-generated method stub
MyProperties p = MyProperties.getInstance();
if (null != p) {// 连接本机的netty服务器
String ip = "127.0.0.1";
int port = p.getNetty_port();
if (null != ip && port >= 0) {
Socket socket = null;
try {
socket = new Socket();
System.out.println("ip: " + ip + " port:" + port);
socket.connect(new InetSocketAddress(ip, port), 6 * 1000);// 还是给一个连接超时6秒
TimeUtils.sleepSeconds(6);// 睡眠6秒再退出,足够的时间给netty处理连接了
} catch (Exception e) {
LoggerUtils.error(logger, e.toString());
} finally {
if (null != socket) {// 主动关闭连接
try {
socket.close();
} catch (IOException e) {
}
}
} // try...catch...finally结束
} // if结束
}
}
}).start();
};
public static Connection getObject() {
return objectQueue.poll();
}
}
2)谁往里面放连接呢?
大体的代码是:
// 4)尝试创建连接conn
Connection conn = null;
int lastIndex = taskPath.lastIndexOf("/");
String target = taskPath.substring(lastIndex + 1);
String[] elements = target.split(":");
String ip = elements[0];
int port = Integer.valueOf(elements[1]);
try {
LoggerUtils.debug(logger, "target machine:" + ip + ":" + port);
conn = ConnectionFactory.makeObject(ip, port, taskData, currentPath, binlogPositionPath, fn, fp);
} catch (Exception e) {
LoggerUtils.debug(logger, e.toString());
conn = null;
}
// conn = null;
if (null != conn) {// 创建成功
LoggerUtils.debug(logger, "create socket succeed: " + conn.getSocketChannel());
NettyQueue.addObject(conn);
}
public static Connection makeObject(String ip, int port, String data, String runningPath, String binlogPositionPath,
String initialFilename, long initialPosition) {
Connection myConn = null;
if (null != ip && port >= 0) {
try {
// 在这里创建具体的对象,注意这里的用法
SocketAddress sAddress = new InetSocketAddress(ip, port);
SocketChannel sChannel = SocketChannel.open(sAddress);
sChannel.configureBlocking(false);// 非阻塞
myConn = new Connection(sChannel,
ConnectionAttributes.parse(data).setIpPort(ip, port).setRunningZKPath(runningPath)
.setBinlogPositionZKPath(binlogPositionPath)
.setClientId(Long.parseLong(ip.replaceAll(".", "") + port))
.updateBinlogNameAndPosition(initialFilename, initialPosition));
} catch (Exception e) {
LoggerUtils.error(logger, e.toString());
}
}
// 无论如何,都返回连接,失敗則返回null
return myConn;
}
3)谁负责取出来呢?
注意我们在第一部分是触发了一个连接本地127.0.0.1的一个连接,这个实际是连接本地的Netty服务器。
需要知道Netty如何启动的
EventLoopGroup workerGroup = new NioEventLoopGroup(worker);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(MyNioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 2048).childHandler(new ChildChannelHandler());
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
// 等待服务端监听端口关闭
LoggerUtils.info(logger, "netty server start ok.");
f.channel().closeFuture().sync();
重点就在MyNioServerSocketChannel.class
public class MyNioServerSocketChannel extends NioServerSocketChannel {
private static final Logger logger = LogManager.getLogger(MyNioServerSocketChannel.class);
// 继承已经有的类,用于干预连接
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
// logger.debug("\ndoReadMessages(...) enter....\n触发了新的连接...开始准备2阶段提取");
// logger.debug("buf :" + buf);
// LoggerUtils.debug(logger, new Exception().toString());
// 原始部分,直接关闭
try {
SocketChannel tempCh = javaChannel().accept();
tempCh.close();
} catch (Exception e) {
}
// 移花接木
Connection connection = NettyQueue.getObject();
try {
if (connection != null) {
MyNioSocketChannel channel = new MyNioSocketChannel(this, connection.getSocketChannel(),
connection.getAttributes());
buf.add(channel);
LoggerUtils.debug(logger, "db conn is as follows: " + connection.getSocketChannel());
// logger.debug("buf :" + buf);
return 1;
}
} catch (Throwable t) {
LoggerUtils.info(logger, "Failed to create a new channel from an accepted socket." + t);
try {
connection.close();
} catch (Throwable t2) {
LoggerUtils.info(logger, "Failed to close a socket." + t2);
}
}
return 0;
}
}
所以,到这里应该就知道了,用了一种诱骗式的方法,
巧妙的绕过了Netty的平常只接收主动连接自己的服务端socket的限制
使得任意客户端连接都可以纳入netty的管理范围
同时对netty框架本身不构成任何入侵性。