背景
从 2010 年开始,工作中先后开发了 3 套 RPC 框架:Json-RPC、Notify、JRPC。每次都是对上个框架做了大的重构,这个期间学习了很多优秀的 RPC 框架(Dubbo、Motan、Sofa-RPC、gRPC),吸收了很多先进的思想,同时也是对架构的不断否定再升级。
为了给同学们讲解 RPC 原理,将之前看到过、实现过的 RPC 框架特性做了一个总结,后来不小心就写成了一个高效能的框架Focus。希望对想学习 RPC 的同学有所帮助,当然,你也可以把它应用到你的项目中。
概述
Focus是下一代跨语言、轻量级 RPC 框架。旨在帮助快速的开发微服务应用程序,简化多运行环境下的 RPC 编程,可以很轻松的支持云端编程和移动端编程。
特性
跨语言支持。同时支持多种串行化协议:Jackson 和 Protobuff。
模块化 API。模块化的客户端和服务端 API、可扩展的系统架构核心小于 1 MB。
分层架构。合理严谨的分层(包括 API 层、代理层、调用层、协议层、传输层)使得依赖最小化、可控。
可插拔的服务发现机制。使用Clutch支持 Zookeeper,Consul,Nacos 等参见注册中心。
可插拔的调用过滤机制。可实现 Logging、Tracing、Metrics、熔断等功能。
支持同步调用、异步调用、泛化调用。满足各种场景下的不同诉求。
高效的自定义协议。二进制消息交换协议Photon和Focus的 RPC 协议。
不同级别的服务控制。全局级别、服务级别的序列化、压缩、超时、重试设置,方法级别的超时、重试设置。
Spring boot 集成支持友好。简化 Spring 应用的集成、开发难度。
快速开始
快速开始给一个基础的例子,大家感受一下,同一台机器上的客户端和服务端的 RPC 编程是多么的简单。更多的例子可以参考例子工程focus-example。
运行快速启动的最低要求是:
JDK 1.8 及以上
Maven 或 Gradle 工程管理工具
同步调用
创建 maven 工程 focus-quickstart 并添加依赖到 pom 文件。
<dependency> <groupId>com.dinstone.focus</groupId> <artifactId>focus-server</artifactId> <version>0.9.7</version></dependency><dependency> <groupId>com.dinstone.focus</groupId> <artifactId>focus-client</artifactId> <version>0.9.7</version></dependency><dependency> <groupId>com.dinstone.focus</groupId> <artifactId>focus-serialize-json</artifactId> <version>0.9.7</version></dependency>
复制代码
创建 FooService 接口类。
package focus.quickstart;
public interface FooService { public String hello(String name);}
复制代码
创建 FooService 实现。
package focus.quickstart.server;
import focus.quickstart.FooService;
public class FooServiceImpl implements FooService {
public String hello(String name) { return "hello " + name; }
}
复制代码
创建 Focus server 并导出服务。
package focus.quickstart.server;
import java.io.IOException;
import com.dinstone.focus.server.FocusServer;import com.dinstone.focus.server.ServerOptions;import com.dinstone.loghub.Logger;import com.dinstone.loghub.LoggerFactory;
import focus.quickstart.FooService;
public class FocusServerBootstrap {
private static final Logger LOG = LoggerFactory.getLogger(FocusServerBootstrap.class);
public static void main(String[] args) { ServerOptions serverOptions = new ServerOptions().listen("localhost", 3333) .setEndpoint("focus.quickstart.server"); FocusServer server = new FocusServer(serverOptions);
// exporting service server.exporting(FooService.class, new FooServiceImpl());
// server.start(); LOG.info("server start"); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } server.destroy(); LOG.info("server stop"); }
}
复制代码
创建 Focus client 导入 FooService 并进行 RPC 调用。
package focus.quickstart.client;
import com.dinstone.focus.client.ClientOptions;import com.dinstone.focus.client.FocusClient;
import focus.quickstart.FooService;
public class FocusClientBootstrap {
public static void main(String[] args) { ClientOptions option = new ClientOptions().setEndpoint("focus.quickstart.client").connect("localhost", 3333); FocusClient client = new FocusClient(option); try { FooService fooService = client.importing(FooService.class); String reply = fooService.hello("dinstone"); System.out.println(reply); } finally { client.destroy(); } }
}
复制代码
异步调用
创建异步调用服务接口 FooAsyncService。
package focus.quickstart.client;
import java.util.concurrent.CompletableFuture;
public interface FooAsyncService { public CompletableFuture<String> hello(String name);}
复制代码
创建 Focus client 导入 FooAsyncService 并进行异步 RPC 调用。
package focus.quickstart.client;
import java.util.concurrent.CompletableFuture;
import com.dinstone.focus.client.ClientOptions;import com.dinstone.focus.client.FocusClient;import com.dinstone.focus.client.ImportOptions;
public class FocusClientAsyncCallBootstrap {
public static void main(String[] args) throws Exception { ClientOptions option = new ClientOptions().setEndpoint("focus.quickstart.client").connect("localhost", 3333); FocusClient client = new FocusClient(option); try { ImportOptions importOptions = new ImportOptions("focus.quickstart.FooService"); FooAsyncService fooService = client.importing(FooAsyncService.class, importOptions); CompletableFuture<String> replyFuture = fooService.hello("dinstone"); System.out.println(replyFuture.get()); } finally { client.destroy(); } }
}
复制代码
泛化调用
泛化调用不需要创建客户端接口类。简单了不是吗?!
创建 Focus client 导入 GenericService 并进行 同步/异步 RPC 调用。
package focus.quickstart.client;
import java.util.concurrent.CompletableFuture;
import com.dinstone.focus.client.ClientOptions;import com.dinstone.focus.client.FocusClient;import com.dinstone.focus.client.GenericService;
public class FocusClientGenericCallBootstrap {
public static void main(String[] args) throws Exception { ClientOptions option = new ClientOptions().setEndpoint("focus.quickstart.client").connect("localhost", 3333); FocusClient client = new FocusClient(option); try { GenericService genericService = client.generic("focus.quickstart.FooService", null, 3000);
String reply = genericService.sync(String.class, "hello", "dinstone"); System.out.println("sync call reply : " + reply);
CompletableFuture<String> replyFuture = genericService.async(String.class, "hello", "dinstone"); System.out.println("async call reply : " + replyFuture.get()); } finally { client.destroy(); } }
}
复制代码
总结
评价一个 RPC 框架是否优秀,有 3 个基本标准:
简单易用:不需要过多的研究使用文档,看看 API 就能用起来,框架代码不侵入业务代码。
高效扩展:能适应绝大多数场景,即使有特殊场景,也可以通过设置或扩展来快速适应。
性能优越:“高性能”永远是一个绕不开的关注点,框架的实现也是编码能力的提现。
Focus 框架在三个方面做了很多的努力,坚持最小侵入性,坚持面向对象的 SOLID 原则,追求极简协议。希望对框架学习的同学有帮助!
对Focus 框架有了初步的感受,你是否原因关注它,改进它、并持续应用它呢?是的话请给Focus 点赞吧!
评论