1.设置netty配置文件
package com.citydo.netty;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
/**
* 存储整个工程的全局配置
* @author liuyazhuang
*
*/
public class NettyConfig2 {
/**
* 存储每一个客户端接入进来的channel对象
* */
public static ChannelGroup g=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
2.webSocket请求发送处理消息的方法
package com.citydo.netty;
import java.util.Date;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
public class WebSocketHandler extends SimpleChannelInboundHandler<Object>{
private WebSocketServerHandshaker Handshaker;
private static final String WEB_SOCKET_URL="ws//127.0.0.1:8888/websocket";
//客户端与服务端创建连接的时候调用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
NettyConfig2.g.add(ctx.channel());
System.err.println("客户端与服务端连接开启");
}
//客户端与服务端断开调用
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
NettyConfig2.g.remove(ctx.channel());
System.err.println("客户端与服务端连接关闭");
}
//服务端口接收客户端发送过来数据结束之后调用
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
//工程出现异常的时候调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
//服务端处理客户端websocket 请求的核心方法
@Override
protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {
// TODO Auto-generated method stub
//处理客户端向服务段发送http握手请求的业务
if (msg instanceof FullHttpRequest) {
handHttpRequest(context, (FullHttpRequest)msg);
}else if(msg instanceof WebSocketFrame) {
//处理webSocket连接业务
HandWebsocketFrame(context, (WebSocketFrame)msg);
}
}
/**
* 处理客户端与服务端之前websocket业务
* @param ctx
* @param frame
*/
private void HandWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame){
if (frame instanceof CloseWebSocketFrame) {
Handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
}
//判断是否是ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
//判断是否二进制消息,如果是二进制消息 抛出异常
if (!(frame instanceof TextWebSocketFrame)) {
System.err.println("目前不支持二进制消息");
throw new RuntimeException("["+this.getClass().getName()+"] 不支持消息");
}
//返回应答消息
//获取客户端想入服务器端发送的消息
String request=((TextWebSocketFrame) frame).text();
System.err.println("服务端收到客户端的消息===》"+request);
TextWebSocketFrame tws=new TextWebSocketFrame(new Date().toString()
+ctx.channel().id()
+"=====>>>"
+request);
//群发的功能 服务端向每一个连接上来的客户端群发消息
NettyConfig2.g.writeAndFlush(tws);
}
/**
* 处理客户端向服务发送http握手请求的业务
* @param ctx
* @param request
*/
@SuppressWarnings("unused")
private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest request) {
if (!request.getDecoderResult().isSuccess()||!"websocket".equals(request.headers().get("Upgrade"))) {
sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
WebSocketServerHandshakerFactory wsFactory=new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
Handshaker=wsFactory.newHandshaker(request);
if (Handshaker==null) {
WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
}else {
Handshaker.handshake(ctx.channel(), request);
}
}
/**服务端向客户端响应消息
* @param ctx
* @param req
* @param res
*/
@SuppressWarnings("unused")
private void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,
DefaultFullHttpResponse res) {
if (res.getStatus().code()!=200) {
ByteBuf buf=Unpooled.copiedBuffer(res.getStatus().toString(),CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
}
//服务器向客户端发送数据
ChannelFuture f=ctx.channel().writeAndFlush(res);
if (res.getStatus().code()!=200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
}
3.初始化各个组件
package com.citydo.netty;
import com.imooc.netty.MyWebSocketHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* 初始化连接时候的各个组件
* @author nick
*
*/
public class WebSocketChannelHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel e) throws Exception {
e.pipeline().addLast("http-codec",new HttpClientCodec());
e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
e.pipeline().addLast("handler",new MyWebSocketHandler());
}
}
4.启动Websocket的方法
package com.citydo.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Application {
public static void main(String[] args) {
EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
EventLoopGroup workLoopGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossLoopGroup, workLoopGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new WebSocketChannelHandler());
System.err.println("服务开启等待客户端连接.....");
Channel channel = serverBootstrap.bind(8888).channel();
channel.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
//退出程序
bossLoopGroup.shutdownGracefully();
workLoopGroup.shutdownGracefully();
}
}
}
netty连接池:
在高并发情况下,做微服务架构,底层服务与服务之间的调用,一般是采用TCP长连接,长连接的性能当然要比短连接至少高十倍以上,但在采用长连接发送消息的时候,假如你的业务并发量比较高,一条通道处理不过来的话,可以选择连接池的方式,每次请求都可以从池里取通道,假如你使用Netty客户端来发送的话,连接池有三种方案选择:
第一种:采用Netty自带的连接池,优点是可以连接多个服务,但没有设置空闲连接数,以及空闲时间等而且绑定了netty客户端
第二种: Apache的commons-pool2这种连接池,这种连接池比较通用,不管使用Netty还是普通的socket都可以使用,同时也有空闲数设置,空闲时间设置,但要去连接多个不同的服务器的话,还需要自己扩展。
第三种:自己采用信号量和并发队列编写一个连接池,自己编写的连接池,虽然通用化没有那么完美,但可以灵活调用,加一些额外的特征。