一、项目启动核心源码
上一篇文章说了项目是如何启动的,仍然与springboot一样,找到启动类Bootstarp.java,本篇讲讲启动之前到底干了些什么,还是使用的web服务器吗如tomcat等?
1.ProxyConfigurationLoader.load 就是将我们填写的配置文件server.yaml 或者 config-encrypt.yaml 来解析出来封装为YamlProxyConfiguration 对象。
2.下面的port 、addresses 都是配置文件中定义的,项目的端口是什么
3.核心方法 new BootstrapInitializer().init 初始化了些什么,具体参考下面
4.new ShardingSphereProxy().start(port, addresses); 使用了netty作为服务器
public static void main(final String[] args) throws IOException, SQLException {
BootstrapArguments bootstrapArgs = new BootstrapArguments(args);
// 读取配置文件,创建Yaml对象
YamlProxyConfiguration yamlConfig = ProxyConfigurationLoader.load(bootstrapArgs.getConfigurationPath());
int port = bootstrapArgs.getPort().orElseGet(() -> new ConfigurationProperties(yamlConfig.getServerConfiguration().getProps()).getValue(ConfigurationPropertyKey.PROXY_DEFAULT_PORT));
List<String> addresses = bootstrapArgs.getAddresses();
// 初始化 -如:将配置持久化到存储设备等等
new BootstrapInitializer().init(yamlConfig, port, bootstrapArgs.getForce());
boolean cdcEnabled = null != yamlConfig.getServerConfiguration().getCdc() && yamlConfig.getServerConfiguration().getCdc().isEnabled();
if (cdcEnabled) {
new CDCServer(addresses, yamlConfig.getServerConfiguration().getCdc().getPort()).start();
}
// 类似netty启动,里面注册了很多消息处理器,规定了客户端发送消息服务端到底如何接收
new ShardingSphereProxy().start(port, addresses);
}
1.核心方法 new BootstrapInitializer().init
其中唯一的核心方法就是createContextManager(proxyConfig, modeConfig, port, force);创建上下文的管理器,剩下的几乎都是将配置信息封装为元数据对象。
public void init(final YamlProxyConfiguration yamlConfig, final int port, final boolean force) throws SQLException {
ModeConfiguration modeConfig = null == yamlConfig.getServerConfiguration().getMode() ? null : new YamlModeConfigurationSwapper().swapToObject(yamlConfig.getServerConfiguration().getMode());
ProxyConfiguration proxyConfig = new YamlProxyConfigurationSwapper().swap(yamlConfig);
ContextManager contextManager = createContextManager(proxyConfig, modeConfig, port, force);
ProxyContext.init(contextManager);
contextManagerInitializedCallback(modeConfig, contextManager);
ShardingSphereProxyVersion.setVersion(contextManager);
}
=》createContextManager()
首先将所有配置信息-比如数据源信息、加密信息、整个服务的全局配置信息全部封装到ContextManagerBuilderParameter 这个总配置类中,接下来使用了Java的SPI机制来获取 到底是否使用配置中心来将配置进行持久化,根据在Server.yaml中的
mode:
type: Cluster
来进行区分,使用配置中心就走ClusterContextManagerBuilder,
private ContextManager createContextManager(final ProxyConfiguration proxyConfig, final ModeConfiguration modeConfig, final int port, final boolean force) throws SQLException {
// 将配置文件中的 各个参数封装为对象,比如 加密信息、server.yaml中各个配置信息等 封装到 ContextManagerBuilderParameter ,总配置类
ContextManagerBuilderParameter param = new ContextManagerBuilderParameter(modeConfig, proxyConfig.getDatabaseConfigurations(),
proxyConfig.getGlobalConfiguration().getRules(), proxyConfig.getGlobalConfiguration().getProperties(), proxyConfig.getGlobalConfiguration().getLabels(),
createInstanceMetaData(proxyConfig, port), force);
ContextManagerBuilder contextManagerBuilder = null == modeConfig
? RequiredSPIRegistry.getRegisteredService(ContextManagerBuilder.class)
: TypedSPIRegistry.getRegisteredService(ContextManagerBuilder.class, modeConfig.getType());
return contextManagerBuilder.build(param);
}
=》ClusterContextManagerBuilder.build()
1.getClusterPersistRepository() ,判断是哪一种配置中心,并执行init初始化,建立连接继续使用第二个SPI机制,来获取某个配置中心,以ZK为例,使用的是 CuratorFramework 来操作ZK,使用CuratorFrameworkFactory.builder()来设置zk的连接信息、超时时间、重试次数,以及新增、修改、删除等操作封装为 ZookeeperRepository。
目前支持的种类
2.persistConfigurations(),将之前配置信息封装的ContextManagerBuilderParameter 对象进行持久化操作,根路径为proxy_1,然后比如全局的规则信息 /proxy_1/rules ,全局的参数信息 /proxy_1/rules
3.继续下面就是RegistryCenter 注册中心的创建,里面定义了 使用的配置中心信息、以及调用zk在线注册方法、zk中节点发生变化watch机制、根据某个key从zk中获取信息。
4. persistMetaData()
向ZK中持久化元数据信息,这部分信息包括代理数据库,这个库下面的所有表以及字段信息、索引信息
这两部分代码一个是对表持久化、一个是对表下面的字段进行持久化
public void persist(final String databaseName, final String schemaName, final ShardingSphereSchema schema) {
if (schema.getTables().isEmpty() && schema.getViews().isEmpty()) {
addSchema(databaseName, schemaName);
}
tableMetaDataPersistService.persist(databaseName, schemaName, schema.getTables());
}
private void persistTableData(final String databaseName, final String schemaName, final String tableName, final Collection<YamlShardingSphereRowData> rows) {
tableRowDataPersistService.persist(databaseName, schemaName, tableName, rows);
}
5.setContextManagerAware() 继续向下又创建一个上下文管理器,将元数据信息和实例信息封装,然后设置到 ClusterModeContextManager ,整个集群的上下文管理器
@Override
public ContextManager build(final ContextManagerBuilderParameter param) throws SQLException {
// 判断是哪一种配置中心,并执行init初始化,建立连接
ClusterPersistRepository repository = getClusterPersistRepository((ClusterPersistRepositoryConfiguration) param.getModeConfiguration().getRepository());
MetaDataPersistService persistService = new MetaDataPersistService(repository);
// 将配置信息进行持久化,也就是conf下的配置文件,实际上都是server.yaml 以及 config-encry.yaml的配置信息
persistConfigurations(persistService, param);
// 定义了注册中心对象,里面大体都是操作zk的
RegistryCenter registryCenter = new RegistryCenter(repository, new EventBusContext(), param.getInstanceMetaData(), param.getDatabaseConfigs());
InstanceContext instanceContext = buildInstanceContext(registryCenter, param);
if (registryCenter.getRepository() instanceof InstanceContextAware) {
((InstanceContextAware) registryCenter.getRepository()).setInstanceContext(instanceContext);
}
MetaDataContexts metaDataContexts = MetaDataContextsFactory.create(persistService, param, instanceContext, registryCenter.getStorageNodeStatusService().loadStorageNodes());
persistMetaData(metaDataContexts); // 持久化:这个库下面的所有表信息、字段信息、主键等信息
ContextManager result = new ContextManager(metaDataContexts, instanceContext);
setContextManagerAware(result);
registerOnline(persistService, registryCenter, param, result);
return result;
}
=>registerOnline()
其中前面的几个方法都是向zk中注册信息,比如应用启动的时候的版本、它的一个唯一id信息,目前来说没什么大用.但是其中最后两个方法很重要。
6.new ContextManagerSubscriberFacade(persistService, registryCenter, contextManager);
在这里面创建了一系列的订阅者,来订阅当zk中的配置得到修改后就会发生变化,比如 zk中的/proxy_1/metadata/postgres/versions/0/data_sources 中的配置内容发送变化时,首先基于zk的watch机制监听,然后由谷歌的eventbus的post进行通知。
public ContextManagerSubscriberFacade(final MetaDataPersistService persistService, final RegistryCenter registryCenter, final ContextManager contextManager) {
new ConfigurationChangedSubscriber(persistService, registryCenter, contextManager);
new ResourceMetaDataChangedSubscriber(contextManager);
new DatabaseChangedSubscriber(contextManager);
new StateChangedSubscriber(registryCenter, contextManager);
new ProcessListChangedSubscriber(registryCenter, contextManager);
new CacheEvictedSubscriber(contextManager.getInstanceContext().getEventBusContext());
}
7.registryCenter.onlineInstance()也是注册信息,只不过注册的是自己的节点信息,CuratorCache
主要用于缓存ZooKeeper中的节点信息。可以对单个节点信息进行缓存,也可以对子树(包括该节点及其所有子节点)的信息进行缓存。CuratorCache还支持注册监听器,这样在特定事件发生时,如节点信息发生变化等,就可以实时收到通知。
public void watch(final String key, final DataChangedEventListener listener) {
CuratorCache cache = caches.get(key);
if (null == cache) {
cache = CuratorCache.build(client, key);
caches.put(key, cache);
}
// client代表了与ZooKeeper服务器的连接,key则指定了需要缓存的节点路径,而listener则用于监听节点的变化事件。
CuratorCacheListener curatorCacheListener = CuratorCacheListener.builder()
.forTreeCache(client, (framework, treeCacheListener) -> { // 对treeCache进行监听
Type changedType = getChangedType(treeCacheListener.getType()); // 获取变更类型
if (Type.IGNORED != changedType) { // 如果不是忽略类型
listener.onChange(new DataChangedEvent(treeCacheListener.getData().getPath(), // 发出变更事件
new String(treeCacheListener.getData().getData(), StandardCharsets.UTF_8), changedType));
}
}).build();
cache.listenable().addListener(curatorCacheListener);
start(cache);
}
private void registerOnline(final MetaDataPersistService persistService, final RegistryCenter registryCenter, final ContextManagerBuilderParameter param, final ContextManager contextManager) {
contextManager.getInstanceContext().getInstance().setLabels(param.getLabels());
contextManager.getInstanceContext().getAllClusterInstances().addAll(registryCenter.getComputeNodeStatusService().loadAllComputeNodeInstances());
new ContextManagerSubscriberFacade(persistService, registryCenter, contextManager); // 创建上下文管理 ,创建一系列对象订阅 如数据源修改等
registryCenter.onlineInstance(contextManager.getInstanceContext().getInstance()); // 注册一下当前节点的信息,持久化到 zk,也就是zk配置中 nodes 节点 ,并且使用zk的watch机制
}
2.总结
本篇主要介绍了,再真正的启动netty时之前所做的准备工作,1.将配置信息解析成对象 2.将配置信息进行持久化
里面使用了很多层对象来进行封装,比较复杂,并且使用了Java的spi机制来降低系统的耦合,不想在硬编码里面写死具体的实现,而是希望由服务提供者来选择使用那种实现方式。
所以阅读这部分源码学会了,如果当有很多个实例类时,可以使用Java spi机制,来降低系统的耦合。
后续如何启动netty敬请下回分解: