前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TAF 必修课(三):Server 启动全过程

TAF 必修课(三):Server 启动全过程

原创
作者头像
serena
修改2021-08-03 14:56:08
2.3K0
修改2021-08-03 14:56:08
举报
文章被收录于专栏:社区的朋友们社区的朋友们

作者:温昂展

一、程序入口

要说清楚后面的一些TAF实现需要完整地考虑整个框架体系,因此本节先对TAF服务端的整个启动过程进行解析,同时探讨一些相关问题。

不得不说TAF框架的代码质量还是挺高的,业界良心!

光看程序逻辑就比较清晰,如下代码,上来直接找到startUp包下,Main程序,就是这么的简单直白,与优雅,这就是taf服务启动的入口:

代码语言:txt
复制
package com.qq.cloud.taf.server.startup;

public class Main {

    /**
     * The only way to start TaServer.
     * 
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) {
        new Server().startUp(args);
    }
} 

好,点击跳转进入startUp方法,打开新世界的大门。

二、启动过程

TAF服务的整个启动流程可概括如下,下面分点叙述。

[1504579037296_4491_1504579037576.png]
[1504579037296_4491_1504579037576.png]

三.、加载配置文件

这个过程主要通过ConfigurationManager类来完成,通过读取xml配置文件,通过Config工具类读取/usr/local/app/taf/${app}.${service}/conf/${app}.${service}.config.conf,该文件包括了TAF服务端配置、各个obj服务配置以及客户端连接器的配置。

配置管理类的封装也比较直观,代码如下:

代码语言:txt
复制
public class ServerConfig {

    private String application;     
    private String serverName;      
    private Endpoint local;
    ....
	//若干配置项

    private LinkedHashMap<String, ServantAdapterConfig> servantAdapterConfMap;      
    private CommunicatorConfig communicatorConfig;         

    public ServerConfig load(Config conf) {
	...
	}
	//省略
}

类图如下

[1504579075386_8874_1504579075552.png]
[1504579075386_8874_1504579075552.png]

四、初始化Logger

TAF使用的日志框架是内部的 j4log, 原理上和log4j 日志框架很相像,定义了5个日志级别,3种日志类型,分别如下:

代码语言:txt
复制
/**
 * 日志级别
 */
public static enum Level {
	DEBUG(0), INFO(1), WARN(2), ERROR(3), FATAL(4);
	private int value;

	private Level(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
}

/**
 * 日志类型
 */
public static enum LogType {
	LOCAL(0), REMOTE(1), ALL(2);

	private int value;

	private LogType(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
}

从名字也可以看出来是什么意思,默认配置也是通过读取property配置文件来定义的,配置文件名为 j4log.property,这是写死在代码里面的,不可修改。

有个地方需要注意的是,j4log还有一个重置日志级别,提供给TAF管理平台使用。该日志级别:NONE,作用是关闭日志不打印,它没有将其作为一个单独的日志级别,而是使用状态标记NONE。

之前的文章中也提到了,TAF有一个远程的日志服务中心,因此这里的j4log即支持将日志输出到本地,也支持调用LogPrx将日志打到远程,且两者可以同时使用。同时出于可靠策略,TAF在远程写入多次失败的时候会自动转为输出本地。在实现上,当然是在本地内存中保存在一个缓冲队列,定期的批量再写到磁盘或远程啦。

另外,由于是服务端程序,TAF会将标准输出重定向为stdout.log文件,错误输出重定向到stderr.log文件,另外默认初始化出几个常用的日志类,如:tafserver.log , nami_core.log, 代码是这样写的:

代码语言:txt
复制
public static void init() {
        System.setOut(new PrintStream(new LoggingOutputStream(Logger.getLogger(STDOUT_log_NAME)), true));
        System.setErr(new PrintStream(new LoggingOutputStream(Logger.getLogger(STDERR_LOG_NAME)), true));
}

五、 拉起运营服务

前面已经提到,TAF另一个大重要的模块就是提供了一整套完备的运营服务,服务端除了拉取路由服务没有使用到之外,其他服务都有应用,代码实现上可以看到封装了很多的Helper,其下则是使用了各个服务的远程代理对象,各个服务如下,

  • LogPrx: 这个前面已经提到了,可以远程收集各个业务打印的远程日志,用于分析
  • ConfigHelper:远程拉取业务服务的配置文件
  • NotifyHelper:收集服务状态变更、异常信息以及业务自定义的异常信息等重要信息,然后对这些信息进行监控告警,比如上报连接数据库失败的信息
  • PropertyReportHelper:用于上报业务一些特定的属性数据,比如内存大小、队列大小,然后进行监控
  • NodeHelper: 收集业务的心跳上报,服务版本号
  • ServerStatHelper:对业务Server之间调用的流量、平均耗时、异常率、超时率进行统计监控

另外还有一个发布服务Patch:存放和获取业务的发布包,给node进行业务Server的发布,这是由主控发起的服务,这里不涉及。

还是直接看图比较清晰:

[1504579128997_4773_1504579129175.png]
[1504579128997_4773_1504579129175.png]

可能这里你就会产生一个疑问, 为什么要分成这么多的服务而不是集中起来呢?

我的理解是: 首先,功能上各个运营服务是不一样的; 其次,怎么让这些服务不和TAF处理客户端请求和业务处理的代码解构呢?当然是单独开启一个周期执行的线程统一管理啦!此时,可想而知每个服务需要执行的周期不尽相同,另一方面,各个服务需要的机器资源也是有差异的,当然是分开部署好些。

以上大概就是开发运营一体化理念的吧。

当然,详细探究以上几个涉及监控上报的服务设计实现还是有必要的,后面再具体提及。

六、 容器初始化

经过前面的一系列初始化工作,当前环境已经具备了配置信息、日志记录、运营服务了,此时就应该将业务侧的代码和资源加载进来了,这个实现TAF采用了容器的概念,容器初始化所需的配置信息在service.xml 文件读取。

[1504579147304_947_1504579147577.png]
[1504579147304_947_1504579147577.png]

如图,AppContainer即为装载类资源的容器,loadApp的具体执行执行过程总结如下:

1.构建一个类加载器AppClassLoader,加载BasePath/ROOT目录下的资源文件

2.读取BasePath/service.xml文件,从中加载listener,加载om管理命令servant

  1. 加载业务services,并设置最大负载max-load,线程池大小threads,队列大小queuecap,触发监听器的appServiceStarted
  2. 取得支持的Jce协议(之后可以考虑扩展到其他协议)的所有Services信息放入AnalystManager, 存储接口方法名对应方法参数和返回值
  3. 触发监听器执行appContextStarted方法

七、初始化网络IO线程模型

这个就是上一节提到的Reactor多线程+ NIO模型啦,下节将对如何处理客户端请求做详细的说明,其中,Session提供了一个对连接上下文的封装和抽象,提供socket读写API,后面启动的SessionManager即是对session的回收管理。

具体可看下图:

[1504579162648_4900_1504579162948.png]
[1504579162648_4900_1504579162948.png]

另外值得注意的是,TAF怎么实现多协议的支持呢?

在代码实现上,可以看到它分别启动了四个NIO服务器:

代码语言:txt
复制
/**
 * Start NIO server
 * 
 * @throws Exception
 */
protected void startNIOServer() throws IOException {
	// 1. Start main port server, and it is required.
	startMainServer(host, port);
	// 2. Start admin port and it is required.
	startMainServer(adminHost, adminPort);

	// 3. Start extended UDP server, and it is optional.
	startUDPServer();

	// 4. Start extended TCP server, and it is optional.
	startTCPServer();
}

这就厉害了我的哥,其中第一个MainServer就是监听处理走JCE协议的客户端请求,JCE下面的传输层其实是TCP可靠的传输,第二个绑定在adminHost上的MainServer用于处理和Node节点交互的管理命令;

另外两个则用于支持HTTP协议的请求,包括TCP和UDP两种协议,在实现上只需要绑定对应的编解码器Codec就可以了,后文重点关注JCE协议。

还有一个值得注意的地方,目前去理解当前这样的实现,发现多个Obj都会对同一个监听端口复用,也就是说在服务接收到请求的时候是没有办法直接区分开是那个Obj的请求的,只有在后面的业务线程处理时才会分发到各个服务Obj上处理。这里也就是为什么之后看到TAF对于服务连接数的管理,目前是按整个服务的总量来做的, 具体实现下节再详细展开

八、启动Session管理器

管理器实现类为SessionManagerImpl,它可提供Session的注册和回收,同时还可以绑定监听器来实现一些功能逻辑,目前主要了解其完成的两个工作:

  1. 管理Session,定期回收过期SessionTimeout连接
  2. 注册监听器记录连接数,当连接总数大于整个服务的总量限制maxConns关闭该创建的连接

九、Shutdown Hook

终于讲完啦,最后当然不能忘记服务退出时候的资源释放,这部分功能可以通过注册JVM 的 Runtime shutdownHook加以实现,主要是关闭seletor管理器和app容器,代码可以这样写:

代码语言:txt
复制
private void registerServerHook() {
        Runtime.getRuntime().addShutdownHook(run() -> {
            
                try {
                    // 1. Stop SelectorManager
                    if (mainSelectorManager != null) {
                        mainSelectorManager.stop();
                    }
                    if (udpSelectorManager != null) {
                        udpSelectorManager.stop();
                    }

                    System.out.println("[SERVER] server stopped successfully.");

                    // 2. Stop Container
                    if (container != null) {
                        container.stop();
                    }
                } catch (Exception ex) {
                    System.err.println("The exception occured at stopping server...");
                }
            
        });
    }

感谢阅读,有错误之处还请不吝赐教。

这一节的理解要特别感谢一下terry浩哥,少走了不少弯路

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、程序入口
  • 二、启动过程
  • 三.、加载配置文件
  • 四、初始化Logger
  • 五、 拉起运营服务
  • 六、 容器初始化
  • 七、初始化网络IO线程模型
  • 八、启动Session管理器
  • 九、Shutdown Hook
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档