有一阵子没有更新源码解析了,最近关注了一个微服务框架SOFA RPC这是蚂蚁金服的一个开源框架,地址在:https://github.com/sofastack/sofa-rpc, 这个框架还是一个朋友推荐的,看了第一部分它的发布流程没想到代码写的很精简易懂,封装性也很好,更大的激发了我看它的乐趣,那我们就一起一步步去学习下它的整个微服务框架的构成和实现吧!
一、 首先我们先从github fork下SOFA RPC这个项目,作者使用的是5.5版本,环境搭建及整体模型介绍参考:https://www.jianshu.com/p/1eeee833bd5d, 本地编译好那就准备工作都完成了,开始分析吧!服务发布现在有两种方式,一种是SOFA RPC发布方式,还有一种是使用SOFA BOOT,本系列文章都使用SOFA RPC方式分析,SOFA BOOT我们后面开专栏讲解是怎么封装SPRING BOOT的,OK 贴发布代码:
/** 需要发布的接口
**/
public interface HelloService {
public String sayHello(String name);
}
/** 发布接口的实现类
**/
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello , "+name;
}
}
/** SOFA RPC 发布流程
**/
public class RpcServer {
public static void main(String[] args) {
//1、 构建RegistryConfig 注册配置
RegistryConfig registryConfig = new RegistryConfig().setProtocol("sofa").setAddress("127.0.0.1:8080");
RegistryConfig registryConfig1 = new RegistryConfig().setProtocol("zookeeper").setAddress("127.0.0.1:2181");
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
registryConfigs.add(registryConfig);
registryConfigs.add(registryConfig1);
// 2、构建ServerConfig 服务配置
List<ServerConfig> serverConfigs = new ArrayList<ServerConfig>();
ServerConfig serverConfig = new ServerConfig().setProtocol("bolt").setPort(12200).setDaemon(false);
ServerConfig serverConfig1 = new ServerConfig().setProtocol("rest").setPort(12200).setDaemon(false);
serverConfigs.add(serverConfig);
serverConfigs.add(serverConfig1);
// 3、构建发布配置
ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setApplication(new ApplicationConfig()
.setAppName("paul"))
.setInterfaceId(HelloService.class.getName())
.setRef(new HelloServiceImpl())
.setServer(serverConfigs)
.setRegistry(registryConfig);
// 4、正式发布
providerConfig.export();
}
}
前面接口类和实现类我们不必要多说,我们主要讲它怎么发布的,那我们进一步看下发布流程做了哪几件事
1、构建RegistryConfig 注册配置
RegistryConfig registryConfig = new RegistryConfig().setProtocol("sofa").setAddress("127.0.0.1:8080");
RegistryConfig registryConfig1 = new RegistryConfig().setProtocol("zookeeper").setAddress("127.0.0.1:2181");
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
首先是构建RegistryConfig注册配置,这个主要是用来注册服务使用的,可以支持多个协议:
文中举例使用了sofa 和 zookeeper 的注册中心,我们点进去源码发现是这样的:
public class RegistryConfig extends AbstractIdConfig implements Serializable
...
发现它是空构造方法,那它真的什么都没做吗?不是!它继承了AbstractIdConfig这个类,我们点进去这个类是这样的:
public abstract class AbstractIdConfig<S extends AbstractIdConfig> implements Serializable {
private static final long serialVersionUID = -1932911135229369183L;
/**
* Id生成器
*/
private final static AtomicInteger ID_GENERATOR = new AtomicInteger(0);
static {
RpcRuntimeContext.now();
}
...
这里我们就发现了,它有一个静态方法,那我们看看这个静态方法干了些什么:
这个也是啥都没做但是我们又发现RpcRuntimeContext又有一个静态方法,点进去看看,终于在这里我们看到它还是做了些事的
static {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Welcome! Loading SOFA RPC Framework : {}, PID is:{}", Version.BUILD_VERSION, PID);
}
// 放入当前的sofa版本号
put(RpcConstants.CONFIG_KEY_RPC_VERSION, Version.RPC_VERSION);
// 初始化一些上下文
initContext();
// 初始化其它模块
ModuleFactory.installModules();
// 增加jvm关闭事件
if (RpcConfigs.getOrDefaultValue(RpcOptions.JVM_SHUTDOWN_HOOK, true)) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("SOFA RPC Framework catch JVM shutdown event, Run shutdown hook now.");
}
destroy(false);
}
}, "SOFA-RPC-ShutdownHook"));
}
}
那我们来一步步看看这个到底做了些什么
首先是 放入当前的sofa版本号,这个没啥说的,都是写死的版本,其次是初始化一些上下文,这里就是设置了全局运行时上下文类RpcRuntimeContext首先初始化上下文自动部署的appId、appName、appInsId以及当前所在文件夹地址appPath信息:
/**
* 初始化一些上下文
*/
private static void initContext() {
putIfAbsent(KEY_APPID, RpcConfigs.getOrDefaultValue(APP_ID, null));
putIfAbsent(KEY_APPNAME, RpcConfigs.getOrDefaultValue(APP_NAME, null));
putIfAbsent(KEY_APPINSID, RpcConfigs.getOrDefaultValue(INSTANCE_ID, null));
putIfAbsent(KEY_APPAPTH, System.getProperty("user.dir"));
}
其中通过配置加载器和操作入口RpcConfigs静态代码块在类加载的时候加载rpc-config-default.json、sofa-rpc/rpc-config.json、META-INF/sofa-rpc/rpc-config.json配置文件获取默认配置、自定义配置信息自定义配置信息存储在全部配置CFG:
static {
init(); // 加载配置文件
}
private static void init() {
try {
// loadDefault
String json = FileUtils.file2String(RpcConfigs.class, "rpc-config-default.json", "UTF-8");
Map map = JSON.parseObject(json, Map.class);
CFG.putAll(map);
// loadCustom
loadCustom("sofa-rpc/rpc-config.json");
loadCustom("META-INF/sofa-rpc/rpc-config.json");
// load system properties
CFG.putAll(new HashMap(System.getProperties())); // 注意部分属性可能被覆盖为字符串
} catch (Exception e) {
throw new SofaRpcRuntimeException("Catch Exception when load RpcConfigs", e);
}
}
接下来就是初始化其它模块了,这个模块我简单写了两个注释,这个不影响阅读,因为里面涉及到SPI,为减少篇幅,会单独写一篇文章来介绍
/**
* 加载全部模块
*/
public static void installModules() {
// 使用ExtensionLoaderFactory获取传入class的ExtensionLoader
ExtensionLoader<Module> loader = ExtensionLoaderFactory.getExtensionLoader(Module.class);
// 获取需要加载的模块配置
String moduleLoadList = RpcConfigs.getStringValue(RpcOptions.MODULE_LOAD_LIST);
for (Map.Entry<String, ExtensionClass<Module>> o : loader.getAllExtensions().entrySet()) {
String moduleName = o.getKey();
Module module = o.getValue().getExtInstance();
// judge need load from rpc option
if (needLoad(moduleLoadList, moduleName)) {
// judge need load from implement
if (module.needLoad()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Install Module: {}", moduleName);
}
module.install();
INSTALLED_MODULES.put(moduleName, module);
} else {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("The module " + moduleName + " does not need to be loaded.");
}
}
} else {
if (LOGGER.isInfoEnabled())