上一篇讲了consumer端,这次说下provider端.不过老是在client/consumer,server/provider名词间胡乱转换,挺烦的.个人觉得站在通信层更适合讲client/server,不过站在整个rpc上来讲,还是consumer/provider比较合适.好了,回到这个provider端来.
首先我们梳理下server主要的功能有哪些.
当然server肯定是用来接受client发来的消息的.那么client想得到的是她的方法调用的结果.所以,server端要做的就是根据消息来找到对应的方法获得调用结果.
这里我们同样从netty server开始讲起:
boot.group(boss, worker)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
final ChannelPipeline pipe = ch.pipeline();
pipe
.addLast("idleStateHandler",
new IdleStateHandler(BeaconConstants.IDLE_READ_TIMEOUT, 0, 0, TimeUnit.SECONDS))
.addLast("lengthDecoder",
new LengthFieldBasedFrameDecoder(BeaconConstants.MAX_LEN,
BeaconConstants.LEN_OFFSET, BeaconConstants.INT_LEN))
.addLast("beaconDecoder", new NettyDecoder())
.addLast("beaconEncoder", new NettyEncoder())
.addLast("beaconServerHandler", serverHandler);
}
};);
f = boot.bind(port).syncUninterruptibly();
netty的server和client格式差不多,但是server端是由俩个NioEventLoopGroup组成:boss和worker,这样命名可以理解为boss只是接收client发来的消息,而具体的消息怎么处理交给worker去做.好处就是降低了每个事件处理任务的复杂度,通过分发任务增加了并发量.然后具体的handler和client端差不多,和client端共享一套编解码机制,idleStateHandler是用来处理心跳的,也是netty原生提供的方法.而我们自定义的serverHandler用来处理我们的具体业务.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
BeaconHandler handler = NettyChannel.getChannel(ctx.channel(), From.SERVER);
try {
handler.receive(msg);
} finally {
if (!ctx.channel().isActive()) {
NettyChannel.removeChannel(ctx.channel());
}
}
}
我们这里来看接受消息的方法 channelRead,和client端几乎一样.这里的BeaconHandler是我们上一篇讲到的我们rpc用来处理一些逻辑的,我们接收到消息后,直接交给handler来处理这些消息.其中NettyChannel是我对BeaconHandler和netty的channel的一个封装,用来对channel的复用管理.
/**
* 每一个channel都会生成对应的BaseChannel,用于对channel的存储
*/
public static final ConcurrentMap<Channel, BeaconHandler> CHANNEL_MAP = new ConcurrentHashMap<>(16);
private Channel channel;
public NettyChannel(Channel channel) {
this.channel = channel;
}
public static BeaconHandler getChannel(Channel ch, From client) throws Exception {
BeaconHandler beacon = CHANNEL_MAP.get(ch);
if (beacon == null) {
NettyChannel nc = new NettyChannel(ch);
if (From.CLIENT == client) {
BeaconHandler b = new BeaconClientHandler(new BeaconClientChannel(nc));
CHANNEL_MAP.putIfAbsent(ch, (beacon = b));
} else {
BeaconHandler b = new BeaconServerHandler(new BeaconServerChannel(nc));
CHANNEL_MAP.putIfAbsent(ch, (beacon = b));
}
}
return beacon;
}
上一篇讲到我们baseChannel的核心就是netty的channel,所以我们看到所有的封装的核心都是围绕channel展开.
我们回到handler上面来.server接受到消息交给handler处理.当然这里和consumer一样,我定义的beaconServerHandler依旧木有做啥子事情.最终还是让我们的BeaconServerChannel来处理消息.
@Override
protected void doReceive(Object message) throws Exception {
this.addTask(new Runnable() {
@Override
public void run() {
RpcResponse resp = new RpcResponse();
Object result = null;
try {
try {
// 处理收到client信息
Class<?> target = Class.forName(req.getInterfaceImpl());
// 根据信息,找到实现类
for (Method d : target.getDeclaredMethods()) {
if (d.getName().equals(req.getMethodName())) {
result = d.invoke(target.newInstance(), req.getParams());
break;
}
}
} catch (Exception bize) {
// 非rpc异常
throw new BizException(bize);
}
} catch (Exception e) {
LOG.error("error->" + e);
resp.setException(e);
}
// 调用发送给client发送结果
baseChannel.send(resp.setResult(result));
}
});
}
这里同样是通过线程池来处理消息,可以借用netty server的思想,我们的netty channel只负责接收消息,具体的逻辑处理交给rpc的channel来做,这样就可以增加netty channel的处理能力,所以我们采用线程池来做.然后这里逻辑也不难,通过获取消息里面的信息,找到实现类,经过反射获取结果,然后直接调用baseChannel发送给client端.
ok,server端差不多就这样了,主要是说了核心的代码思想,也可以看到我们借用了很多netty的写代码思路.希望我也可以设计出好的代码来.
具体的实现参照我的github:https://github.com/dressrosa/beacon