netty整合springboot
1.原理说明
在网络编程领域,Netty是Java的一个优秀的框架,他将java的复杂和难以使用的关于NIO框架进行了封装,使其隐藏在易用的api后面。总之,编写高性能的网络服务,使用Netty就很方便。
Netty传输框架,接收传输信息,截取访问路径,并使用java反射原理,得到对应spingboot 的controller类的ReqestMapping方法上,以此使用高性能网络服务,进行数据传输。
2.代码说明
2.1 建springboot项目
(1). pom.xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
<scope>provided</scope>
</dependency>
<!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<compile>test</compile>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>(2).main方法
@SpringBootApplication
public class AppRun {
public static void main(String[] args) {
SpringApplication.run(AppRun.class, args);
}
}(3).controller
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/hello")
public String testHello(String message){
System.out.println("####$$$$$$$$$$$########:"+message);
return "test";
}
}(4).application.yml
server:
port: 8889(5).测试说明
访问:http://192.168.18.106:8889/hello/hello?message=”ss”
返回: test2.2.创建netty项目
(1).main方法改造
实现了CommandLineRunner接口,并重写了run方法,以便在springboot启动后就立即启动netty服务。
@SpringBootApplication
public class AppRun implements CommandLineRunner{
@Autowired
NettyServer nettyServer; //netty服务器
@Autowired
CommandBuilder commandBuilder;//注解解析器
public static void main(String[] args) {
SpringApplication.run(AppRun.class, args);
}
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
commandBuilder.setUp(); //注解解析启动
nettyServer.run();//netty服务器启动
}
}(2)java反射演示
java bean
public class Command {
public void print(String ss){
System.out.println("string "+ss);
}
public void print(){
System.out.println("string null ");
}
public void print(int ss){
System.out.println("int "+ss);
}
}反射获取方法
public class TestCommand {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//获取类
Class<?> forName = Command.class;
//类实例化对象
Object newInstance = forName.newInstance();
//方法 (方法名称,参数类型)
Method declaredMethod = forName.getDeclaredMethod("print",int.class);
//java invoke使用
declaredMethod.invoke(newInstance, 2);
//返回 int 2
}
}(3).注解解析器
反射需要的命令参数java bean
@Data
public class CommandAction {
private Object className;
private Method method;
private String path;
public CommandAction(){}
public CommandAction(String path,Object cmd,Method method){
this.path = path;
this.method = method;
this.className = cmd;
}
}注解解析类
@Component
public class CommandBuilder implements ApplicationContextAware{
private Logger logger = LoggerFactory.getLogger(CommandBuilder.class);
private ApplicationContext applicationContext; //获取上下文
//封装反射需要的命令参数
private Map<String, CommandAction> map = new HashMap<String, CommandAction>();
public void setApplicationContext(ApplicationContext context) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = context;
}
public void setUp(){
//通过上下文获取项目的 所有注解@RestController
Map<String, Object> controllerBean = this.applicationContext.getBeansWithAnnotation(RestController.class);
for(String key: controllerBean.keySet()){
//获取类
Object cmd = controllerBean.get(key);
// @org.springframework.web.bind.annotation.RequestMapping
// (path=[], headers=[], method=[], name=, produces=[], params=[], value=[/hello], consumes=[])
//全路径变量
String route = "";
//获取类上注解@RequestMapping
RequestMapping annotation = cmd.getClass().getAnnotation(RequestMapping.class);
if(annotation!=null){
//取前段路径
route = annotation.value()[0];
}
//获取当前类的所有方法
Method[] methods = cmd.getClass().getMethods();
for(Method m: methods){
try {
//获取当前方法的注解@RequestMapping
RequestMapping value = m.getAnnotation(RequestMapping.class);
if(value!=null){
//取方法路径
route+=value.value()[0];
logger.info("found routes: "+route);
//将路径 和 反射需要的命令参数封装到map
map.put(route, new CommandAction(route, cmd, m));
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
logger.error(e.getMessage());
}
}
}
}
/**
* 通过路径 获取 对应的controller
* @param path
* @return
*/
public CommandAction getCommandAction(String path){
return map.get(path);
}
}(4).netty 服务器
nettyServer
@Component
public class NettyServer {
private Logger logger = LoggerFactory.getLogger(NettyServer.class);
private EventLoopGroup bossgroup; //创建mainReactor
private EventLoopGroup workgroup; //创建subReactor
@Bean
public EventLoopGroup bossGroup(){
bossgroup = new NioEventLoopGroup();
return bossgroup;
}
@Bean
public EventLoopGroup workGroup(){
workgroup = new NioEventLoopGroup();
return workgroup;
}
//将对象 nettyServerHandler 交给spring容器管理
private NettyServerHandler nettyServerHandler;
@Bean
public NettyServerHandler nettyServerHandler(){
nettyServerHandler = new NettyServerHandler();
return nettyServerHandler;
}
//这样不会获取到 ApplicationContext 上下文对象 (则不能使用注解解析器)
// @Bean
// public NettyServerHandler nettyServerHandler(){
// return new NettyServerHandler();
// }
//
@PreDestroy
public void close(){
logger.info("服务器关闭...");
bossgroup.shutdownGracefully();
workgroup.shutdownGracefully();
}
public void run(){
try {
//服务启动引导
ServerBootstrap serverBootstrap = new ServerBootstrap();
//组装NioEventLoopGroup
serverBootstrap.group(bossGroup(), workGroup())
//设置通道类型
.channel(NioServerSocketChannel.class)
//设置连接参数
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_KEEPALIVE, true)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
//配置入栈出栈handler
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder(Charset.forName("UTF-8")));
pipeline.addLast(new StringEncoder(Charset.forName("UTF-8")));
pipeline.addLast("nettyhandler",nettyServerHandler);
}
});
//绑定端口号
ChannelFuture sync = serverBootstrap.bind(8888).sync()
//添加监听
.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
// TODO Auto-generated method stub
if(future.isSuccess()){
logger.info("netty服务器在端口:"+8888+" 启动监听!");
}else{
logger.info(TimeUtils.getNowTime()+"端口:"+8888+" 绑定失败!");
}
}
});
sync.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
logger.error("服务器出现异常:"+e.getCause()+"");
bossgroup.shutdownGracefully();
workgroup.shutdownGracefully();
}finally {
//优雅关闭
bossgroup.shutdownGracefully();
workgroup.shutdownGracefully();
}
}
}nettyServerHandler
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter implements ApplicationContextAware{
private Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
// 同(3)注解解析类
private CommandBuilder commandBuilder;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//通过上下文获取 已经注入的CommandBuilder对象(有且只有这一种方法)
this.commandBuilder = applicationContext.getBean(CommandBuilder.class);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
logger.info("服务器连接客户端成功");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
logger.info("服务器收到的消息:"+msg);
if(msg instanceof String){
String receive = (String)msg;
logger.info("receiveMessage: "+receive);
// "/hello/hello|12";
String[] split = receive.split("[|]");
String cmd = split[0];
String sendMessage = split[1];
logger.info("cmd:"+cmd);
logger.info("sendMessage:"+sendMessage);
try {
CommandAction commandAction = this.commandBuilder.getCommandAction(cmd);
//使用反射原理 将消息发送给 controller
Method method = commandAction.getMethod();
Object invoke = method.invoke(commandAction.getClassName(), sendMessage);
logger.info("返回信息:"+invoke.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
logger.error(""+e.getCause());
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
logger.info("服务器异常:"+cause.getMessage());
ctx.channel().close();
}
}(5).netty客户端连接测试
TestClient
public class TestClient {
public static void main(String[] args) {
EventLoopGroup workgroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workgroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringEncoder(Charset.forName("UTF-8")));
pipeline.addLast(new StringDecoder(Charset.forName("UTF-8")));
pipeline.addLast(new ClientHandler());
}
});
ChannelFuture future = bootstrap.connect("192.168.18.106",8888).sync();
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
// TODO Auto-generated method stub
if(future.isSuccess()){
System.out.println("客户端连接成功");
}else{
System.out.println("客户端连接失败");
}
}
});
String message = "/hello/hello|12";
System.out.println("客户端发送的消息:"+message);
future.channel().writeAndFlush(message);
future.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}ClientHandler
public class ClientHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
System.out.println("客户端收到的消息:"+msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(ctx, cause);
System.out.println("客户端异常:"+cause.getMessage());
}
}
文章标题:netty整合springboot
发布时间:2019-11-29, 15:02:14
最后更新:2019-11-29, 15:02:15