一: 交互流程
说明:
- client stub 接收到方法调用后 对参数、方法签名封装成易网络传输的消息体
- client stub 将消息进行编码,并发送至服务端
- server stub 接收消息并进行解码
- server stub 根据解码结果调用本地的服务
- server本地服务应答server stub
- servier stub 将执行结果进行封装编码至消费方
- 消费方进行解码,告知client stub 的调用者
二: 相关类介绍
HelloService
:提供者 与消费者 所使用的API
public interface HelloService {
String hello(String request);
}
NettyServer
:提供者服务启动项目,其中的NettyServerHandler 见下。
public class NettyServer {
public static void startServer(String hostname, int port) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(4);
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(hostname, port).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
NettyServerHandler
:调用本地服务处理类型,其中msg暂时以interface#method#params1=value1¶ms2=value2 的方式进行硬编码规定。
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//获取客户端发送消息,并发转发到本地method
System.out.println("PARAMS msg = " + msg);
//模拟协议快速检查 interface#method#params1=value1¶ms2=value2
if (checkUrls(msg.toString())) {
String[] urls = msg.toString().split("#");
if (urls[0].equals("HelloService") && urls[1].equals("hello")) {
String invokeRes = new HelloServiceImpl().hello(urls[2]);
ctx.writeAndFlush(invokeRes);
System.out.println("finished,res = "+invokeRes);
}
}
}
//simple check
private Boolean checkUrls(String url) {
return url.contains("#");
}
}
HelloServiceImpl
:调用本地服务实现类
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String request) {
System.out.println(" interface from call PARAMS :" + request);
String res = new Date() + " echo from server :";
return res + request;
}
}
ClientBootstrap
:客户端启动类
public class ClientBootstrap {
public static final String protocolPrefix = "HelloService#hello#";
public static void main(String[] args) {
NettyClient client = new NettyClient();
HelloService helloServiceProxy = (HelloService) client.getProxyBean(HelloService.class, protocolPrefix);
String result = helloServiceProxy.hello("say hello");
System.out.println("main result = "+result);
}
}
NettyClient
:客户端 netty 启动类
public class NettyClient {
private static ExecutorService executorService = Executors.newFixedThreadPool(2);
private static NettyClientHandler client;
public Object getProxyBean(Class serviceClass, final String providerUrls) {
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),//class loader
new Class[]{serviceClass},//impl Interfaces
(proxy, method, args) -> { //InvocationHandler
if (client == null) initClient();
client.setParams(providerUrls + args[0]);
return executorService.submit(client).get();
});
}
public static void initClient() {
client = new NettyClientHandler();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(client);
}
});
try {
bootstrap.connect("127.0.0.1", 8080).sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
NettyClientHandler
:客户端对于服务端消息应答处理类
public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
private ChannelHandlerContext context;
private String result;
private String params;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//set context on channel active
context = ctx;
}
@Override
public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("channelRead" + Thread.currentThread().getName() +" bean" +this.hashCode());
result = msg.toString();
System.out.println("receive date = " + new Date());
notify();
}
//提供代理对象调用,发送给server stub , 当read收到结果之前置于wait状态,
@Override
public synchronized Object call() throws Exception {
System.out.println("call" + Thread.currentThread().getName()+" bean" +this.hashCode());
context.writeAndFlush(params);
wait();
return result;
}
void setParams(String params) {
this.params = params;
}
}
三: 总结
使用netty进行原始、最简单的rpc调用实现中:
- client基于netty做网络传输,在收到服务端有可读事件之前,本地调用的方法形成阻塞,直置被可读事件接收。
- client调用rpc方法,使用代理进行调用,减少侵入式。
- 该demo 中 server处理事情小于client很多,对于客户端发送过来的数据报文解析,知晓执行类是哪个,并做相应调用,最终应答即可。
- rpc-demo能通版还能优化的地方大概有一个银河系呢么大,但是随着该demo跑通结束,对Netty有了第一轮初步认识,可做接来下的学习了。