netty5与spring集成,实现配置启动--(一)

来源:互联网 发布:用友u8数据备份 编辑:程序博客网 时间:2024/05/17 08:24

1.使用netty实现可配置化的NIO通讯服务器

2.要求支持多种通讯协议以及长短链接,如http,https,TCP,TLS,MQTT等

3.支持私有协议拓展开发


以上因为是个人demo项目,所以未做其他方面的需求,比如压力测试,集群部署等,这些自己也需要在下下个阶段继续研究(下个阶段主要研究dubbo和完善自己的demo项目)。


1.Main类

首先我们少不了一个Main类,这个是netty启动的类,直接看代码。

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
package com.omen.netty.server;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.omen.netty.server.sysPojo.SystemInfo;
public class Main {
private static Logger log = Logger.getLogger(Main.class);
@Autowired
private SystemInfo systemInfo;
static {
// 先加载spring
log.info("准备载入spring...");
String url = "../classes/omen.xml";
// 保存context
SystemInfo.setCtx(new FileSystemXmlApplicationContext(url));
log.info("载入spring 完毕...");
}
public static void main(String[] args) throws Exception {
IServer iServer = (IServer) SystemInfo.getCtx().getBean("basicServer");
iServer.start();
}
}
 来自CODE的代码片
com.omen.netty.server.Main.java


这里在启动main方法的时候会首先去加载spring配置文件,同时保存context。期间应用了一个systemInfo对象为该系统的系统配置

   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
package com.omen.netty.server.sysPojo;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.omen.netty.utils.StringUtil;
public class SystemInfo {
private static Logger log = Logger.getLogger(SystemInfo.class);
public SystemInfo(String protocolType,Integer port,Integer channelType) throws Exception{
/**
* 基础校验
*/
if(StringUtil.isEmpty(protocolType)){
throw new Exception("protocolType is not setted");
}
if(port == null){
throw new Exception("port is not setted");
}
if(channelType == null){
throw new Exception("channelType is not setted");
}
this.protocolType=protocolType.toUpperCase();
this.port=port;
this.isSsl=false;
this.channelType=channelType;
}
public SystemInfo(String protocolType,Integer port,Integer channelType,
Integer bossGroupSize,Integer workerGroupSize) throws Exception{
/**
* 基础校验
*/
if(StringUtil.isEmpty(protocolType)){
throw new Exception("protocolType is not setted");
}
if(port == null){
throw new Exception("port is not setted");
}
if(channelType == null){
throw new Exception("channelType is not setted");
}
this.protocolType=protocolType.toUpperCase();
this.port=port;
this.isSsl=false;
this.channelType=channelType;
this.bossGroupSize=bossGroupSize;
this.workerGroupSize=workerGroupSize;
}
public SystemInfo(String protocolType,Integer port,Integer channelType, boolean isSsl,String jksPath,
String jksPwd) throws Exception{
/**
* 基础校验
*/
if(StringUtil.isEmpty(protocolType)){
throw new Exception("protocolType is not setted");
}
if(port == null){
throw new Exception("port is not setted");
}
if(isSsl){
if(StringUtil.isEmpty(jksPath)){
throw new Exception("jksPath type is not setted");
}
if(StringUtil.isEmpty(jksPwd)){
throw new Exception("jksPwd type is not setted");
}
}
if(channelType == null){
throw new Exception("channelType is not setted");
}
this.protocolType=protocolType.toUpperCase();
this.port=port;
this.isSsl=isSsl;
this.jksPath=jksPath;
this.jksPwd=jksPwd;
this.channelType=channelType;
}
public SystemInfo(String protocolType,Integer port,Integer channelType, boolean isSsl,String jksPath,
String jksPwd,Integer bossGroupSize,Integer workerGroupSize) throws Exception{
/**
* 基础校验
*/
if(StringUtil.isEmpty(protocolType)){
throw new Exception("protocolType is not setted");
}
if(port == null){
throw new Exception("port is not setted");
}
if(isSsl){
if(StringUtil.isEmpty(jksPath)){
throw new Exception("jksPath type is not setted");
}
if(StringUtil.isEmpty(jksPwd)){
throw new Exception("jksPwd type is not setted");
}
}
if(channelType == null){
throw new Exception("channelType is not setted");
}
this.protocolType=protocolType.toUpperCase();
this.port=port;
this.isSsl=isSsl;
this.jksPath=jksPath;
this.jksPwd=jksPwd;
this.channelType=channelType;
this.bossGroupSize=bossGroupSize;
this.workerGroupSize=workerGroupSize;
}
private static String protocolType;
private static Integer port;
private static Boolean isSsl;
private static String jksPath;
private static String jksPwd;
private static Integer channelType;
private static ApplicationContext ctx;
private static EventLoopGroup bossGroup ;
private static EventLoopGroup workerGroup;
private static Integer bossGroupSize;
private static Integer workerGroupSize;
public static void printSysInfo(){
log.info("**************SYSTEM INFO******************");
log.info("protocolType : " + protocolType);
log.info("port : " + port);
log.info("channelType : " + channelType + " (0=NIO 1=OIO)");
log.info("isSsl : " + isSsl);
if(!StringUtil.isEmpty(jksPath))
log.info("jksPath : " + jksPath);
if(!StringUtil.isEmpty(jksPwd))
log.info("jksPwd : " + jksPwd);
if(bossGroupSize!=null)
log.info("bossGroupSize : " + bossGroupSize);
if(workerGroupSize!=null)
log.info("workerGroupSize: " + workerGroupSize);
log.info("**************SYSTEM INFO******************");
}
public static void shutDownGraceFully(){
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
public static String getProtocolType() {
return protocolType;
}
public static void setProtocolType(String protocolType) {
SystemInfo.protocolType = protocolType;
}
public static Integer getPort() {
return port;
}
public static void setPort(Integer port) {
SystemInfo.port = port;
}
public static Boolean getIsSsl() {
return isSsl;
}
public static void setIsSsl(Boolean isSsl) {
SystemInfo.isSsl = isSsl;
}
public static String getJksPath() {
return jksPath;
}
public static void setJksPath(String jksPath) {
SystemInfo.jksPath = jksPath;
}
public static String getJksPwd() {
return jksPwd;
}
public static void setJksPwd(String jksPwd) {
SystemInfo.jksPwd = jksPwd;
}
public static Integer getChannelType() {
return channelType;
}
public static void setChannelType(Integer channelType) {
SystemInfo.channelType = channelType;
}
public static ApplicationContext getCtx() {
return ctx;
}
public static void setCtx(ApplicationContext ctx) {
SystemInfo.ctx = ctx;
}
public static Integer getBossGroupSize() {
return bossGroupSize;
}
public static void setBossGroupSize(Integer bossGroupSize) {
SystemInfo.bossGroupSize = bossGroupSize;
}
public static EventLoopGroup getBossGroup() {
return bossGroup;
}
public static void setBossGroup(EventLoopGroup bossGroup) {
SystemInfo.bossGroup = bossGroup;
}
public static EventLoopGroup getWorkerGroup() {
return workerGroup;
}
public static void setWorkerGroup(EventLoopGroup workerGroup) {
SystemInfo.workerGroup = workerGroup;
}
public static Integer getWorkerGroupSize() {
return workerGroupSize;
}
public static void setWorkerGroupSize(Integer workerGroupSize) {
SystemInfo.workerGroupSize = workerGroupSize;
}
public static void main(String[] args) {
}
}
 来自CODE的代码片
com.omen.netty.server.sysPojo.SystemInfo.java


systemInfo里面涵盖了多个构造函数,这个在后面会有解释(想通过spring直接配置系统参数),目前有个更好的想法就是通过配置map去完成配置系统参数,但是目前还没改好,改好以后会更新代码。


IServer为server接口

  1  2  3  4  5  6  7  8  9 10 11
package com.omen.netty.server;
public interface IServer {
public void start() throws Exception;
public void stop() throws Exception;
public void restart() throws Exception;
}
 来自CODE的代码片
com.omen.netty.server.IServer.java


我们的实现类

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
package com.omen.netty.server;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import org.springframework.beans.factory.annotation.Autowired;
import com.omen.netty.server.factory.ServerChannelFactory;
import com.omen.netty.server.sysPojo.SystemInfo;
public class BasicServer implements IServer {
private Channel acceptorChannel;
@Autowired
ServerChannelFactory serverChannelFactory;
@Override
public void start()throws Exception{
try{
acceptorChannel = serverChannelFactory.createAcceptorChannel();
acceptorChannel.closeFuture().sync();
}finally{
//优雅退出,释放线程组资源
SystemInfo.shutDownGraceFully();
}
}
@Override
public void stop()throws Exception {
try{
if(acceptorChannel!=null)
acceptorChannel.close().addListener(ChannelFutureListener.CLOSE);
}finally{
//优雅退出,释放线程组资源
SystemInfo.shutDownGraceFully();
}
}
@Override
public void restart()throws Exception {
stop();
start();
}
}
 来自CODE的代码片
com.omen.netty.server.BasicServer.java

在我们的实现类中,start方法和stop方法最终都会调用systemInfo的shutDownGracefully以便释放资源。其中start当中会等待服务端链路关闭以后main函数才退出。


2.几个工厂

先看代码

channel工厂

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
package com.omen.netty.server.factory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.apache.log4j.Logger;
import com.omen.netty.exception.SysErrException;
import com.omen.netty.server.sysPojo.ProtocolType;
import com.omen.netty.server.sysPojo.SystemInfo;
public class ServerChannelFactory {
private static Logger log = Logger.getLogger(ServerChannelFactory.class);
public Channel createAcceptorChannel() throws SysErrException{
final ServerBootstrap serverBootstrap = ServerBootstrapFactory.createServerBootstrap();
serverBootstrap.childHandler(getChildHandler());
log.info("创建Server...");
try {
ChannelFuture channelFuture = serverBootstrap.bind(SystemInfo.getPort()).sync();
channelFuture.awaitUninterruptibly();
if (channelFuture.isSuccess()) {
SystemInfo.printSysInfo();
return channelFuture.channel();
} else {
String errMsg="Failed to open socket! Cannot bind to port: "+SystemInfo.getPort()+"!";
log.error(errMsg);
throw new SysErrException(errMsg);
}
} catch (Exception e){
throw new SysErrException(e);
}
}
private static ChannelInitializer<SocketChannel> getChildHandler() throws SysErrException{
if(ProtocolType.HTTP.equals(SystemInfo.getProtocolType())
||ProtocolType.HTTPS.equals(SystemInfo.getProtocolType())){
return (ChannelInitializer<SocketChannel>)SystemInfo.getCtx().getBean("httpServerInitializer");
}
else if(ProtocolType.TCP.equals(SystemInfo.getProtocolType()))
return (ChannelInitializer<SocketChannel>)SystemInfo.getCtx().getBean("tcpServerInitializer");
else if(ProtocolType.CUSTOM.equals(SystemInfo.getProtocolType()))
return (ChannelInitializer<SocketChannel>)SystemInfo.getCtx().getBean("customServerInitializer");
else{
String errMsg="undefined protocol:"+SystemInfo.getProtocolType()+"!";
throw new SysErrException(errMsg);
}
}
}
 来自CODE的代码片
com.omen.netty.server.factory.ServerChannelFactory.java

ServerBootStrap工厂

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
package com.omen.netty.server.factory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import com.omen.netty.server.sysPojo.ChannelType;
import com.omen.netty.server.sysPojo.SystemInfo;
/**
* @author liyidong
* @version 1.0
* @created 2014-12-16 上午11:14:33
* @function:工厂模式 server服务器 ServerBootstrap创建
*/
public class ServerBootstrapFactory {
private ServerBootstrapFactory() {
}
public static ServerBootstrap createServerBootstrap() throws UnsupportedOperationException {
ServerBootstrap serverBootstrap = new ServerBootstrap();
switch (SystemInfo.getChannelType()) {
case ChannelType.NIO:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
serverBootstrap.group(bossGroup, workerGroup);
SystemInfo.setBossGroup(bossGroup);
SystemInfo.setWorkerGroup(workerGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
return serverBootstrap;
//TODO
case ChannelType.OIO:
serverBootstrap.group(new OioEventLoopGroup());
serverBootstrap.channel(OioServerSocketChannel.class);
return serverBootstrap;
default:
throw new UnsupportedOperationException("Failed to create ServerBootstrap, " + SystemInfo.getChannelType() + " not supported!");
}
}
}
 来自CODE的代码片
com.omen.netty.server.factory.ServerBootstrapFactory.java


以上是两个关键类

在ServerBootstrapFactory中

我们创建了两个NioEventLoopGroup实例。NioEventLoopGroup是一个多线程的I/O操作事件循环池,Netty为各种传输方式提供了多种EventLoopGroup的实现。NioEventLoopGroup专门用于网络事件的处理,实际上它们就是Reactor线程组。这里创建两个的原因是一个用于服务端接受客服端的链接,另一个用于进行SocketChannel的网络读写。

我们还在ServerBootstrapFactory中创建了ServerBootstrap对象,它是netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。

传送门,讲解ServerBootstrap工作原理

http://blog.csdn.net/zxhoo/article/details/17532857

接下来我们调用了ServerBootstrap的group方法,将两个NIO线程组当作入参传递到ServerBootstrap,借着设置创建的channel类型为NioServerSocketChannel,他的功能是监听网络事件类型并创建channel。


在ServerChannelFactory中

我们对ServerBootstrap绑定了I/O事件的处理类,它的作用类似于Reactor当中的handler,主要用于处理网络是I/O事件,例如记录日志、对消息进行编解码等。

ServerBootstrap配置完成以后,调用它的bind方法绑定监听端口,随后,调用它的同步阻塞方法sync等待绑定完成。完成之后netty会返回一个channelFuture,它的功能类主要用于异步操作的通知回调。