计划写一个RPC用于把学到的一些理论实践一下。下面记录每天干的事情和用到的知识点。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
重要事情说三遍 RPC地址 请各位START。 非常感谢。
2020-06-18 项目初始化
在gitee上创建了一个项目,为什么不在github上呢?因为连接速度慢。为什么不提高一下速度呢?因为翻墙还得花钱。为什么不花钱呢?因为工资低,为什么工资低呢?因为技术太差。怎么样才能技术好呢? 写一下RPC吧。
目前用到的知识点
- Java SPI。用它来实现面向接口开发。动态获取一些实现类。
- 在rpc中有stubs的概念,服务端和客户端 都用到了,包含一些网络地址等信息。用他来去发起请求,处理请求。
2020-06-23 序列化接口设计
主要功能是将对象序列化后存储到字节数组中,因此添加了计算序列化后的长度的方法,序列化的方法,反序列化的方法,获取序列化对象类型的方法,还有返回数据类型的标识的方法。
2020-06-24 序列化类注册,反序列化方法开发
主要是在序列化包装类加载时将序列化类注册到容器中。
2020-06-28 序列化类开发
- 添加了将类序列化成数组的方法
2010-06-29 启动关闭服务
- 启动关闭服务
2020-06-30
- 注册中心注册服务
2020-07-01
- 测试RPC项目搭建开发,已经开发到服务端
- SPI配置文件
- 响应类
- 传送中的请求
开发中复习了一下信号量的使用
2020-07-02
- Netty实现传输消息客户端
还真得复习复习netty了, 好多东西都得现查。
2020-07-03
这几天的开发主要集中在netty上面,使用netty 来完成进程 之间的消息传递。使用到了自定义协议。
2020-07-06 请求编码器开发
此处要特别注意请求协议的设计,具体体现在Netty中Bytebuf的操作上。
2020-07-07 响应结果添加到future中
在netty的响应pipline中添加处理器,将响应结果添加到响应集合中。
关于CompletableFuture中学习文章请参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650
鸟窝,大牛博客大牛博客!!!!
相当推荐第二个博客,真是大牛大牛大牛。
2020-07-08 Netty 实现消息的传递
这一块的功能主要是在PIPELINE中处理, 重要是要弄懂channel.writeAndFLush()方法。
闪电侠的解析 比较推荐。
2020-07-09 消息处理器和服务服务实现开发
2020-07-12 请求处理器
请求处理器,是服务端接收到请求,执行方法使用的。
Header header = requestCommand.getHeader();
// 从payload中反序列化RpcRequest
RpcRequest rpcRequest = SerializeSupport.parse(requestCommand.getPayload());
try {
// 查找所有已注册的服务提供方,寻找rpcRequest中需要的服务
Object serviceProvider = serviceProviders.get(rpcRequest.getInterfaceName());
if (serviceProvider != null) {
//找到服务提供者,利用java反射机制调用服务的对应方法
String arg = SerializeSupport.parse(rpcRequest.getSerializedArguments());
Method method = serviceProvider.getClass().getMethod(rpcRequest.getMethodName(), String.class);
String result = (String) method.invoke(serviceProvider, arg);
//把结果封装成响应命令并返回
return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId()), SerializeSupport.serialize(result));
}
//如果没找到,返回NO_PROVIDER错误响应
logger.warn("No service Provider of {}#{}(String)!", rpcRequest.getInterfaceName(), rpcRequest.getMethodName());
return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId(), Code.NO_PROVIDER.getCode(), "No provider!"), new byte[0]);
} catch (Throwable t) {
// 发生异常,返回UNKNOWN_ERROR错误响应。
logger.warn("Exception: ", t);
return new Command(new ResponseHeader(type(), header.getVersion(), header.getRequestId(), Code.UNKNOWN_ERROR.getCode(), t.getMessage()), new byte[0]);
}
2020-07-13 RPC桩抽象类开发
protected byte[] invokeRemote(RpcRequest request){
Header header = new Header(ServiceTypes.TYPE_RPC_REQUEST,1, RequestIdSupport.next());
byte[] payload = SerializeSupport.serialize(request);
Command requestCommand = new Command(header,payload);
try {
Command responseCommand = transport.send(requestCommand).get();
ResponseHeader responseHeader = (ResponseHeader) responseCommand.getHeader();
if(responseHeader.getCode() == Code.SUCCESS.getCode()) {
return responseCommand.getPayload();
} else {
throw new Exception(responseHeader.getError());
}
}catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
2020-07-14 动态桩生产工厂
try {
String stubSimpleName = serviceClass.getSimpleName() + "Stub";
String classFullName = serviceClass.getName();
String stubFullName = "com.github.liyue2008.rpc.client.stubs." + stubSimpleName;
String methodName = serviceClass.getMethods()[0].getName();
String source = String.format(STUB_SOURCE_TEMPLATE, stubSimpleName, classFullName, methodName, classFullName, methodName);
// 编译源代码
JavaStringCompiler compiler = new JavaStringCompiler();
Map<String, byte[]> results = compiler.compile(stubSimpleName + ".java", source);
//加载编译好的类
Class<?> clazz = compiler.loadClass(stubFullName, results);
// 把Transport赋值给桩
ServiceStub stubInstance = (ServiceStub) clazz.newInstance();
stubInstance.setTransport(transport);
return (T) stubInstance;
} catch (Throwable t) {
throw new RuntimeException(t);
}
2020-07-15 netty服务启动器
@Override
public void start(RequestHandlerRegistry requestHandlerRegistry, int port) throws Exception {
this.port = port;
this.requestHandlerRegistry = requestHandlerRegistry;
// 接收连接的处理器
EventLoopGroup acceptEventGroup = newEventLoopGroup();
// 处理连接中消息的处理器
EventLoopGroup ioEventGroup = newEventLoopGroup();
// NETTY ChannelPipeline
//TODO 启动器还没有开发完成 ING 1
}
直接在Netty启动类中替换为在Linux系统下的epoll组件
private EventLoopGroup newEventLoopGroup() {
if (Epoll.isAvailable()) {
return new EpollEventLoopGroup();
} else {
return new NioEventLoopGroup();
}
}
2020-07-16 Netty 服务监听器开发
Netty服务开发
public class NettyServer implements TransportServer {
public static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
private int port;
private EventLoopGroup acceptEventGroup;
private EventLoopGroup ioEventGroup;
private Channel channel;
private RequestHandlerRegistry requestHandlerRegistry;
@Override
public void start(RequestHandlerRegistry requestHandlerRegistry, int port) throws Exception {
this.port = port;
this.requestHandlerRegistry = requestHandlerRegistry;
// 接收连接的处理器
EventLoopGroup acceptEventGroup = newEventLoopGroup();
// 处理连接中消息的处理器
EventLoopGroup ioEventGroup = newEventLoopGroup();
ChannelHandler channelHandlerPipeline = newChannelHandlerPipeline();
ServerBootstrap serverBootstrap = newBootStrap(channelHandlerPipeline, acceptEventGroup, ioEventGroup);
Channel channel = doBind(serverBootstrap);
this.acceptEventGroup = acceptEventGroup;
this.ioEventGroup = ioEventGroup;
this.channel = channel;
}
}
服务提交方处理请求的Netty执行器开发
@ChannelHandler.Sharable
public class RequestInvocation extends SimpleChannelInboundHandler<Command> {
private static final Logger logger = LoggerFactory.getLogger(ResponseInvocation.class);
private final RequestHandlerRegistry requestHandlerRegistry;
public RequestInvocation(RequestHandlerRegistry requestHandlerRegistry) {
this.requestHandlerRegistry = requestHandlerRegistry;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Command request) throws Exception {
RequestHandler requestHandler = requestHandlerRegistry.get(request.getHeader().getType());
if (null != requestHandler) {
Command response = requestHandler.handle(request);
if (null != response) {
ctx.writeAndFlush(response).addListener((ChannelFutureListener) channelFuture -> {
if (!channelFuture.isSuccess()){
logger.warn("Write response failed!", channelFuture.cause());
ctx.channel().close();
}
});
}else {
logger.warn("Response is null!");
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warn("Exception: ", cause);
super.exceptionCaught(ctx, cause);
Channel channel = ctx.channel();
if (channel.isActive()) ctx.close();
}
}
RPC 测试
在服务提供方中编写启动服务Server.java
public static void main(String[] args) throws Exception {
String serviceName = HelloService.class.getCanonicalName();
File tmpDirFile = new File(System.getProperty("java.io.tmpdir"));
File file = new File(tmpDirFile, "simple_rpc_name_service.data");
HelloService helloService = new HelloServiceImpl();
logger.info("创建并启动RpcAccessPoint...");
// TODO 编写SPI 主
try (RpcAccessApi rpcAccessApi = ServiceSupport.load(RpcAccessApi.class);
Closeable ignored = rpcAccessApi.startServer()
) {
NameService nameService = rpcAccessApi.getNameService(file.toURI());
assert nameService!=null;
logger.info("向RpcAccessPoint注册{}服务...", serviceName);
URI uri = rpcAccessApi.addServiceProvider(helloService, HelloService.class);
logger.info("服务名: {}, 向NameService注册...", serviceName);
nameService.registerService(serviceName, uri);
logger.info("开始提供服务,按任何键退出.");
System.in.read();
logger.info("Bye!");
}
}
服务调用方 Client.java
public static void main(String [] args) throws IOException {
String serviceName = HelloService.class.getCanonicalName();
File tmpDirFile = new File(System.getProperty("java.io.tmpdir"));
File file = new File(tmpDirFile, "simple_rpc_name_service.data");
String name = "Master MQ";
try(RpcAccessApi rpcAccessPoint = ServiceSupport.load(RpcAccessApi.class)) {
NameService nameService = rpcAccessPoint.getNameService(file.toURI());
assert nameService != null;
URI uri = nameService.lookupService(serviceName);
assert uri != null;
logger.info("找到服务{},提供者: {}.", serviceName, uri);
HelloService helloService = rpcAccessPoint.getRemoteService(uri, HelloService.class);
logger.info("请求服务, name: {}...", name);
String response = helloService.hello(name);
logger.info("收到响应: {}.", response);
}
}
先启动服务提供方,在启动服务调用方。
最后结果