RPC(RemoteProcedure Call)— 远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程
序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
RPC 能够实现两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样:
常见的 RPC 框架有: 比较知名的如阿里的Dubbo、 google的gRPC、 Go语言的rpcx、 Apache的thrift,Spring 旗下的 SpringCloud。
调用流程:
RPC 的目标就是将 这些步骤都封装起来,用户无需关心这些细节,可以像调用本地方法一样即可
完成远程服务调用
术语说明:在RPC 中, Client 叫服务消费者,Server 叫服务提供者
要求用 Netty 实现一个简单的 RPC 框架 。模仿 dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者的服务,提供者返回一个字符串,消费 者打印提供者返回的数据。底层网络通信使用 Netty4.1.20
实现代码的目录结构如下:
publicinterface包中包含公共的接口
customer和provider分别代表服务消费端和服务提供端,它们都包含一个启动器,其中服务提供端还有接口的具体的实现。
publicinterface包:
public interface HelloService {
String hello(String ping);
}
netty包:
/**
* 服务端
*/
public class NettyServer {
/**
* 启动客户端
*/
public static void startServer(String hostName, int port) {
startServer0(hostName, port);
}
private static void startServer0(String hostName, int port) {
try {
ServerBootstrap bootstrap = new ServerBootstrap();
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
bootstrap.group(eventLoopGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new StringDecoder());
p.addLast(new StringEncoder());
p.addLast(new ServerHandler());
}
});
bootstrap.bind(hostName, port).sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 用于处理请求数据
*/
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 如何符合约定,则调用本地方法,返回数据
if (msg.toString().startsWith(ClientBootstrap.providerName)) {
String result = new HelloServiceImpl()
.hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));
ctx.writeAndFlush(result);
}
}
}
public class NettyClient {
private static ExecutorService executor = Executors
.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static ClientHandler client;
/**
* 创建一个代理对象
*/
public Object getBean(final Class<?> serviceClass,
final String providerName) {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{serviceClass}, (proxy, method, args) -> {
if (client == null) {
initClient();
}
// 设置参数
client.setPara(providerName + args[0]);
return executor.submit(client).get();
});
}
/**
* 初始化客户端
*/
private static void initClient() {
client = new ClientHandler();
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new StringDecoder());
p.addLast(new StringEncoder());
p.addLast(client);
}
});
try {
b.connect("127.0.0.1", 7002).sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ClientHandler extends ChannelInboundHandlerAdapter implements Callable {
private ChannelHandlerContext context;
private String result;
private String para;
/**
* 与服务器的连接已经及建立之后将被调用
*
* @param ctx
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
context = ctx;
}
/**
* 收到服务端数据,唤醒等待线程
*/
@Override
public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) {
result = msg.toString();
notify();
}
/**
* 写出数据,开始等待唤醒
*/
@Override
public synchronized Object call() throws InterruptedException {
context.writeAndFlush(para);
wait();
return result;
}
void setPara(String para) {
this.para = para;
}
}
provider包:
/**
* 实现类
*/
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String msg) {
System.out.println("收到客户端消息: " + msg);
return msg != null ? msg + " -----> hello client." : "hello client.";
}
}
public class ServerBootstrap {
public static void main(String[] args) {
NettyServer.startServer("127.0.0.1", 7002);
}
}
customer包:
public class ClientBootstrap {
public static final String providerName = "HelloService#hello#";
public static void main(String[] args) throws InterruptedException {
NettyClient consumer = new NettyClient();
// 创建一个代理对象
HelloService service = (HelloService) consumer.getBean(HelloService.class, providerName);
for (; ; ) {
Thread.sleep(3000);
System.out.println(service.hello("你好 dubbo ~ "));
}
}
}
运行结果:
服务提供端:
收到客户端消息: 你好 dubbo ~
收到客户端消息: 你好 dubbo ~
收到客户端消息: 你好 dubbo ~
服务调用端:
你好 dubbo ~ -----> hello client.
你好 dubbo ~ -----> hello client.
你好 dubbo ~ -----> hello client.
ntln(service.hello("你好 dubbo ~ "));
}
}
}
运行结果:
服务提供端:
```java
收到客户端消息: 你好 dubbo ~
收到客户端消息: 你好 dubbo ~
收到客户端消息: 你好 dubbo ~
服务调用端:
你好 dubbo ~ -----> hello client.
你好 dubbo ~ -----> hello client.
你好 dubbo ~ -----> hello client.