RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。
NIO與IO區別:
IO流程:
- 先將文件內容從磁盤中拷貝到操作系統buffer
- 再從操作系統buffer拷貝到程序應用buffer
- 從程序buffer拷貝到socket buffer
- 從socket buffer拷貝到協議引擎.
NIO流程:
NIO技術省去了將操作系統的read buffer拷貝到程序的buffer, 以及從程序buffer拷貝到socket buffer的步驟, 直接將 read buffer 拷貝到 socket buffer. java 的 FileChannel.transferTo() 方法就是這樣的實現, 這個實現是依賴於操作系統底層的sendFile()實現。
總結:NIO支持更大的併發量,適合服務端編程。
Netty:
是比較流行的nio框架,用來代替socketServer,以下是netty發送字符串例子。
客戶端:EchoClient
/**
* • 連接服務器 • 寫數據到服務器 • 等待接受服務器返回相同的數據 • 關閉連接
*
*/
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup nioEventLoopGroup = null;
try {
// 客戶端引導類
Bootstrap bootstrap = new Bootstrap();
// EventLoopGroup可以理解爲是一個線程池,這個線程池用來處理連接、接受數據、發送數據
nioEventLoopGroup = new NioEventLoopGroup();
bootstrap.group(nioEventLoopGroup)//多線程處理
.channel(NioSocketChannel.class)//指定通道類型爲NioServerSocketChannel,一種異步模式,OIO阻塞模式爲OioServerSocketChannel
.remoteAddress(new InetSocketAddress(host, port))//地址
.handler(new ChannelInitializer<SocketChannel>() {//業務處理類
@Override
protected void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new EchoClientHandler());//註冊handler
}
});
// 鏈接服務器
ChannelFuture channelFuture = bootstrap.connect().sync();
channelFuture.channel().closeFuture().sync();
} finally {
nioEventLoopGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("localhost", 20000).start();
}
}
EchoClientHandler
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
// 客戶端連接服務器後被調用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客戶端連接服務器,開始發送數據……");
byte[] req = "QUERY TIME ORDER".getBytes();//消息
ByteBuf firstMessage = Unpooled.buffer(req.length);//發送類
firstMessage.writeBytes(req);//發送
ctx.writeAndFlush(firstMessage);//flush
}
// • 從服務器接收到數據後調用
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
throws Exception {
System.out.println("client 讀取server數據..");
// 服務端返回消息後
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("服務端數據爲 :" + body);
}
// • 發生異常時被調用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.out.println("client exceptionCaught..");
// 釋放資源
ctx.close();
}
}
服務端:EchoServer
/**
* •配置服務器功能,如線程、端口 • 實現服務器處理程序,它包含業務邏輯,決定當有一個請求連接或接收數據時該做什麼
*
*/
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
EventLoopGroup eventLoopGroup = null;
try {
//server端引導類
ServerBootstrap serverBootstrap = new ServerBootstrap();
//連接池處理數據
eventLoopGroup = new NioEventLoopGroup();
//裝配bootstrap
serverBootstrap.group(eventLoopGroup)
.channel(NioServerSocketChannel.class)//指定通道類型爲NioServerSocketChannel,一種異步模式,OIO阻塞模式爲OioServerSocketChannel
.localAddress("localhost",port)//設置InetSocketAddress讓服務器監聽某個端口已等待客戶端連接。
.childHandler(new ChannelInitializer<Channel>() {//設置childHandler執行所有的連接請求
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());//註冊handler
}
});
// 最後綁定服務器等待直到綁定完成,調用sync()方法會阻塞直到服務器完成綁定,然後服務器等待通道關閉,因爲使用sync(),所以關閉操作也會被阻塞。
ChannelFuture channelFuture = serverBootstrap.bind().sync();
System.out.println("開始監聽,端口爲:" + channelFuture.channel().localAddress());
channelFuture.channel().closeFuture().sync();
} finally {
eventLoopGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoServer(20000).start();
}
}
EchoServerHandler:
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("server 讀取數據……");
//讀取數據
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("接收客戶端數據:" + body);
//向客戶端寫數據
System.out.println("server向client發送數據");
String currentTime = new Date(System.currentTimeMillis()).toString();
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("server 讀取數據完畢..");
ctx.flush();//刷新後纔將數據發出到SocketChannel
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
二、自定義RPC框架
代碼略,代碼較多,還沒研究明白。