RPC 笔记(3)
这一代的引入了Netty框架,用来开发RPC服务器。有关Netty的知识点可以看Netty介绍,之前的server端用的是阻塞同步(BIO)的形式,性能并不高,所以就使用Netty来做NIO。
Netty的数据吞吐量也是很大的,底层调用的还是java.FileChannel.transferTo,这个底层其实用的是操作系统(Linux)的sendFile() 内核函数,从而实现零拷贝。
Version 3
抽象了RPCClient,这样可以让多种实现方式的client有同样的结构
public interface RPCClient {
RPCResponse sendRequest(RPCRequest request);
}
使用Netty实现的Client
public class NettyRPCClient implements RPCClient{
private static final Bootstrap bootstarp;
private static final EventLoopGroup eventLoopGroup;
private String host;
private int port;
public NettyRPCClient(String host, int port) {
this.host = host;
this.port = port;
}
//初始化Bootstarp
static {
eventLoopGroup = new NioEventLoopGroup();
bootstarp = new Bootstrap();
bootstarp.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new NettyClientInitializer());
}
@Override
public RPCResponse sendRequest(RPCRequest request) {
try {
ChannelFuture channelFuture = bootstarp.connect(host, port).sync();
Channel channel = channelFuture.channel();
channel.writeAndFlush(request);
channel.closeFuture().sync();
AttributeKey<RPCResponse> key = AttributeKey.valueOf("RPCResponse");
RPCResponse rpcResponse = channel.attr(key).get();
System.out.println(rpcResponse);
return rpcResponse;
} catch (InterruptedException e) {
System.out.println("发送失败");
e.printStackTrace();
}
return null;
}
}
Client初始化配置类:
@AllArgsConstructor
public class NettyClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new ObjectDecoder(Class::forName));
pipeline.addLast(new NettyRPCClientHandler());
}
}
Client handler 处理:
public class NettyRPCClientHandler extends SimpleChannelInboundHandler<RPCResponse> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, RPCResponse msg) throws Exception {
//用来延迟返回,因为Netty是异步的,发出请求就会直接返回
AttributeKey<RPCResponse> key = AttributeKey.valueOf("RPCResponse");
ctx.channel().attr(key).set(msg);
ctx.channel().close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Client 测试类:
public class TestClient {
public static void main(String[] args) {
NettyRPCClient nettyRPCClient = new NettyRPCClient("127.0.0.1", 8899);
RPCClientProxy clientProxy = new RPCClientProxy(nettyRPCClient);
BlogService blogService = clientProxy.getProxy(BlogService.class);
System.out.println(blogService.findBlogById(13));
}
}
Netty Server:
@AllArgsConstructor
public class NettyRPCServer implements RPCServer{
private ServiceProvider serviceProvider;
@Override
public void start(int port) {
//netty服务线程组boss负责建立连接,work负责处理业务
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
System.out.println("Netty服务端启动了...");
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new NettyServerInitializer(serviceProvider));
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
@Override
public void stop() {
System.out.println("Netty服务端停止运行...");
}
}
Server 初始化配置类与Client初始化一样。
Server handler:
@AllArgsConstructor
public class NettyRPCServerHandler extends SimpleChannelInboundHandler<RPCRequest>{
private ServiceProvider serviceProvider;
@Override
protected void channelRead0(ChannelHandlerContext ctx, RPCRequest msg) throws Exception {
RPCResponse response = getResponse(msg);
ctx.writeAndFlush(response);
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
RPCResponse getResponse(RPCRequest request){
// 得到服务名
String interfaceName = request.getInterfaceName();
// 得到服务端的相应服务类
Object service = serviceProvider.getService(interfaceName);
// 反射调用方法
Method method = null;
try {
method = service.getClass().getMethod(request.getMethodName(), request.getParamsTypes());
Object invoke = method.invoke(service, request.getParams());
return RPCResponse.success(invoke);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
System.out.println("方法执行错误");
return RPCResponse.fail();
}
}
}
ClientProxy 也需要小改动:
把原来的host 和port 删了,改成了一个client,这样可以使用不同方式实现的Client来实现代理类。
@AllArgsConstructor
public class RPCClientProxy implements InvocationHandler {
private RPCClient rpcClient;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RPCRequest request = RPCRequest.builder().interfaceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.params(args).paramsTypes(method.getParameterTypes()).build();
RPCResponse response = rpcClient.sendRequest(request);
return response.getData();
}
public <T>T getProxy(Class<T> clazz){
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T)o;
}
}
图来了,有点小乱: