起源:业务代码里的第一行注册

我们在业务代码里看到了这样一行:

void MyServiceApp::initialize()

{

addServant<MyServiceServantImp>(

ServerConfig::Application + "." + ServerConfig::ServerName + ".MyServiceServantObj"

);

}

第一个疑问冒出来了: addServant 是什么?是add到哪里?会初始化吗?


第一层:addServant 是什么

顺着代码往下找,发现 addServant 不是全局函数,而是 Application 类的成员函数模板。

Application 是 TARS 框架的核心类,就是 main 函数里 g_app 的类型。它在初始化阶段调用业务代码的 initialize(),业务代码在里头向框架"注册"Servant。

// Application.h —— Application::addServant 是包装层

template<typename T>

void addServant(const string &id)

{

// 只有一个参数 id,this 和 check 是自动补的

ServantHelperManager::getInstance()->addServant<T>(id, this, true);

}

这里产生第二个疑问: this 是什么?为什么要传 check = true

  • this 就是 g_app,即 Application 单例指针
  • check = true 表示要校验配置文件里是否声明了对应的 adapter,没有就抛异常

所以 Application::addServant<T>(id) 只是个便捷入口,它帮你补上了 this(框架对象)和 check(配置校验),然后转发给真正的执行者 ServantHelperManager::addServant<T>(id, this, true)


第二层:ServantHelperManager::addServant 做了什么

找到真正的执行者:

// ServantHelper.h —— 底层真正执行

template<typename T>

void addServant(const string &id, Application *application, bool check = false)

{

if (check && _servant_adapter.end() == _servant_adapter.find(id))

{

// 校验:id 必须在配置文件的 adapter 列表里存在

throw runtime_error("[TARS]ServantHelperManager::addServant " + id + " not find adapter.");

}

// 关键:将 creator 存入 map

_servant_creator[id] = new ServantCreation<T>(application);

}

第三个疑问: _servant_creator 是什么?ServantCreation<T> 是什么?为什么 application 要传进去?


第三层:为什么要"工厂模式"—— ServantCreation

顺着 _servant_creator 找到它的类型:

// ServantHelper.h

map<string, ServantHelperCreation*> _servant_creator;

是个 map,key 是 id(比如 "MyApp.MyServer.MyServantObj"),value 是 ServantHelperCreation*

而 ServantCreation<T> 是它的子类:

template<class T>

struct ServantCreation : public ServantHelperCreation

{

ServantCreation(Application *application) : _application(application) {}

ServantPtr create(const string &s)

{

T *p = new T;

p->setName(s);

p->setApplication(_application);

return p;

}

Application *_application;

};

疑问又来了:

3.1 为什么用工厂模式,而不是直接 new?

因为注册发生在 main() 阶段,但请求处理发生在 Handle 线程启动之后。这两步之间有时间差:

main() 阶段:

addServant<MyServiceServantImp>("obj")

→ new ServantCreation<MyServiceServantImp>(g_app) ← 只是把 creator 存起来

Handle 线程启动时(晚得多):

→ 调用 creator->create("obj")

→ new MyServiceServantImp ← 这个时候才真正 new

注册阶段只负责把"怎么 new"的配方(creator)存好,具体创建延迟到 Handle 线程启动时才执行。

3.2 三行代码分别是什么含义?

第一行——构造函数:

ServantCreation(Application *application) : _application(application){}

接受一个 Application* 参数,用初始化列表把它存进成员变量 _application。这里 this 指向 g_app 指针。

第二行——create() 工厂方法:

ServantPtr create(const string &s) { T *p = new T; p->setName(s); p->setApplication(_application); return p; }

工厂方法,每次调用都 new 一个新的 T 实例,然后:

  • setName(s) — 设置 servant 名称(adapter 名字)
  • setApplication(_application) — 把 g_app 指针绑进来,这样 Servant 内部能访问全局配置

第三行——成员变量:

Application *_application;

存储 g_app 指针,供 create() 使用。就是个普通的成员变量。

整体含义一句话: 注册时存下 g_app 指针,启动时 new 出 Servant 实例并把 g_app 绑定进去。

几个要点:

  1. 注册阶段没有线程,所以不能直接 new——工厂把"配方"先存起来
  2. Handle 线程启动时,每个线程独立调用 creator->create(),自然就拿到了自己的实例
  3. 不需要额外同步——_servant_creator map 只在 initialize() 阶段写(单线程),读都在 Handle 线程启动之后,没有竞争

换句话说,工厂模式在这里解决的不只是"怎么 new",而是**"在哪条线程 new"**的问题。


第四层:Handle 线程模型——线程数、实例数、线程安全

这是最核心的问题,也是今天下午讨论最多的。

4.1 有多少个 Handle 线程?

默认 5 个,可以在配置文件中修改。这个数量是进程级别的,同一个进程里所有 Handle 线程共享代码段、全局变量等。

4.2 Servant 实例和线程是什么关系?

每个 Handle 线程启动时,TARS 框架遍历 _servant_creator 里的所有 creator,调用 create()

Handle 线程 1 启动

→ new MyServiceServantImp 实例 A

→ setApplication(g_app)

Handle 线程 2 启动

→ new MyServiceServantImp 实例 B

→ setApplication(g_app)

... 线程 3、4、5 同理

结论:线程数 = Servant 实例数。5 个 Handle 线程 = 5 个独立的 Servant 实例。

4.3 成员变量是线程安全的吗?

这取决于成员变量在哪里:

成员变量 存储位置 线程安全 原因

max_code_lifetime_

每个 Servant 实例自己的

✅ 安全

实例 A 和实例 B 不共享这块内存

std::map cache_

每个 Servant 实例自己的

✅ 安全

同上

db_conn_(栈变量地址)

每个线程自己的栈

✅ 安全

CMySQLPooledConn 在栈上,线程隔离

g_app(全局单例)

进程共享

⚠️ 关注

连接池内部有锁,外部不要乱动

static 变量

进程共享

⚠️ 关注

所有线程可见

结论: 成员变量是线程隔离的,不需要加锁。只有访问 g_app 或 static 变量时才需要考虑同步。


第五层:完整流程回顾(从疑问到闭环)

① main() 启动

g_app.main(argc, argv)

→ 调用业务 MyServiceApp::initialize()

② 注册阶段(initialize 内部)

addServant<MyServiceServantImp>("App.Server.MyServantObj")

→ Application::addServant<T>(id)

→ ServantHelperManager::addServant<T>(id, this, true)

→ _servant_creator["App.Server.MyServantObj"] = new ServantCreation<T>(g_app)

目的:把 "怎么 new" 的配方存起来,延迟到 Handle 线程启动时再执行

③ Handle 线程启动(N 个线程)

线程 1 → creator->create("obj") → new MyServiceServantImp 实例 A → setApplication(g_app)

线程 2 → creator->create("obj") → new MyServiceServantImp 实例 B → setApplication(g_app)

线程 3 → ...

线程 4 → ...

线程 5 → ...

④ 请求处理

客户端请求 → TARS 框架分发到某个 Handle 线程

→ 该线程用自己的 Servant 实例处理

→ 成员变量无竞争,无需加锁


第六层:几个容易踩的坑

❌ 误区1:ServantImp 是单例

ServantImp 不是单例。每个 Handle 线程有自己独立的实例,构造函数会跑 N 次(= 线程数)。如果想在进程级别共享数据,不要用成员变量,要用 static 或全局变量——但这通常意味着你要自己处理线程安全。

❌ 误区2:给所有成员变量加锁

成员变量如果只在单个 Handle 线程内访问,加锁反而增加开销且容易引入死锁。先搞清楚数据流,再决定是否加锁。

❌ 误区3:注册时创建实例

注册阶段只是把 creator 存起来,不是创建实例的时机。在 initialize() 里写业务逻辑、读配置都可以,但不要试图在这里做请求处理——Handle 线程还没启动,请求根本进不来。


附录:关键数据结构一览

类型 位置 作用

Application

框架层

全局单例 g_app,管理生命周期

ServantHelperManager

框架层

管理所有 Servant 的 creator,核心是 _servant_creator map

ServantCreation<T>

框架层

工厂模板,create() 负责 new 实例

MyServiceServantImp

业务层

实现具体业务逻辑,继承框架生成的 Servant 基类

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐