【Chromium中文文档】Profile架构(看看谷歌家的重构)

进程模型

转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Profile_Architecture.html

全书地址

Chromium中文文档 for https://www.chromium.org/developers/design-documents

持续更新ing,欢迎star

gitbook地址:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//

github地址: https://github.com/ahangchen/Chromium_doc_zh

Profile架构

这篇文章描述了一个进行中的设计重构,始于2012年1月。

注意:2013年六月之后,这篇文章需要更新。相关的类被重命名(s/ProfileKeyed/BrowserContextKeyed/)以及移动到components/browser_context_keyed_service中。

Chromium有许多与Profile挂钩的特性,所谓Profile,即一些与当前用户以及跨越多个浏览器window的当前chrome会话。在Chromium刚起步的时候,profile只有一些动态的部分:cookie jar包,历史记录数据库,书签数据库,以及与用户首选项相关的一些东西。在Chromium工程三年的时间里,Profile变成了各个特性的连接点,派生出了一些东西像Profile::GetInstantPromoCounter()或者Profile::GetHostContentSettingsMap()。直到这个文章完成时,在Profile里已经有58个纯虚函数了。

Profile应当是一个最小引用,即一种不拥有实体的句柄对象。

设计目标

  • 我们必须能够分段地转移到新的架构中。每次转移一个服务和特性。我们不能停止地球的转动,不能在一瞬间转换所有的东西。写下这些东西的时候,我们已经将19个服务移出Profile了。
  • 我们应当只对调用端做小的修改,在调用端,Profile被用于在问题中获取服务。
  • 我们必须修复Profile移除这个问题。当我们开始这项工作时,Profile外只有一小部分对象,处于拆分目的手动整理它们是可接受的。但现在我们有了75个组件,我们知道手动拆分整理是不对的,正如这里所写的。有着这么多组件的话,我们不能再依赖手动整理了。
  • 我们必须允许加入编译新特性或者移除旧特性。现在我们有一些chromium的分支,它们不包含在Windows/Mac/Linux Google Chrome标准构建中所有的特性,我们应当允许给出这样一种方式,让这些分支能在不把#ifdef profile.h和profile_impl.h搞得一团糟的情况下,成功编译。这些分支也有他们需要提供的服务。(允许chromium分支添加它们自己的服务也触及我们不能在Profile移除过程中依赖手动整理的原因。)
  • 延伸目标:将不同的特性隔离到它们自己的.a或.so文件里,进一步减小我们奇葩的编译链接时间。

BrowserContextKeyedServiceFactory

浏览器上下文关键服务工厂

旧的方式:Profile接口和ProfileImpl实现

在以前的设计里,服务通常用Profile里的一个访问器来获得:

class ProfileImpl {
  public:
    virtual FooService* GetFooService();
  private:
    scoped_ptr<FooService> foo_service_;
};

在之前的系统里,Profile是由大部分是纯虚访问器组成的结构。Normal(正常),Incognito(匿名)和Testing(测试)profile。

在这个世界里,Profile是所有活动的中心。profile有用它所有的服务,并向外界传递出去。Profile拆分遵循ProfileImpl中对服务排序的任何原则。另外的分支如果想要增加自己的服务或移除不需要的服务,而不修改Profile接口,都是不可能的。

新的方式:BrowserContextKeyedServiceFactory

我们不再让Profile拥有某个service,而是设计了专用的单例FooServiceFactory,比如这样一个最小实现:

class FooServiceFactory : public BrowserContextKeyedServiceFactory {
 public:
  static FooService* GetForProfile(Profile* profile);

  static FooServiceFactory* GetInstance();

 private:
  friend struct DefaultSingletonTraits<FooServiceFactory>;

  FooServiceFactory();
  virtual ~FooServiceFactory();

  // BrowserContextKeyedServiceFactory:
  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
    content::BrowserContext* context) const OVERRIDE;
};

我们有一个通用的BrowserContextKeyedServiceFactory,它用一个由你的BuildServiceInstanceFor()方法提供的对象,执行与profile相关的大部分工作。BrowserContextKeyedServiceFactory为你提供了一个重写接口,让你在响应Profile生命周期事件时,管理你的Service对象的生命周期,并在service依赖的service关闭前,关闭它本身。

一个绝对最小工厂会提供下面的方法:

- 一个static GetInstance()方法,单例指向你的工厂。

- 一个构造函数,关联这个BrowserContextKeyedServiceFactory和ProfileDependencyManager实例,并做DependsOn()声明。

- 一个GetForProfile()方法,包装BrowserContextKeyedServiceFactory,将返回结果转换为你需要的返回值。

- 一个BuildServiceInstanceFor()方法,框架会为每个|profile|调用一次这个方法,它必须返回你的服务的一个合适的实例。

另外,BrowserContextKeyedServiceFactory为你的控制行为提供了这些另外的辅助:

  • RegisterUserPrefs():每个Profile在初始化和用户首选项注册的地方会调用它一次
  • 默认情况下,BCKSF在给定一个Incognito profile时会返回NULL
    • 如果你重写ServiceRedirectedInIncognito()方法并返回true,它会返回与normal Profile相关的服务。
    • 如果你重写ServiceHasOwnInstanceInIncognito()并返回true,它会为incognito profile创建一个新的服务。
  • 默认情况下,BCKSF会延迟创建你的service,如果你重写ServiceIsCreatedWithProfile()并返回true,你的service会与profile一同创建。
  • BCKSF为你在单元测试时提供了多种方式来控制行为。查看头文件了解更多。
  • BCKSF为你一种方式提供一种方式增加并固定移除的和释放的行为。

几种工厂

并非所有对象都有一样的生命周期和内存管理。前面的段落是一个主要的简化版本;基类BrowserContextKeyedBaseFactory定义了大多数常见依赖部分,BrowserContextKeyedServiceFactory是一个具体处理通常对象的工厂。另一个RefcountedBrowserContextKeyedServiceFactory在语义上以及对RefCountedThreadSafe对象的存储上有轻微的差异。

关于复杂度的一个小插曲

上面的这些,在实现上比之前的版本要复杂许多,这是否值得呢?

Yes.

我们绝对应该强调服务的独立性。正如它今天的样子,在多profile模式不再有必要之后,我们没有马上去掉profile,因为在去掉profile时,我们的crash率太高了,不能为用户所接受。我们有75个组件插在profile的生命周期当中,他们之间的依赖图如此复杂以至于我们简单的手动整理不能处理这种复杂度。上面所有可重写的行为之所以存在,是因为它由每个服务,特定的广告,以及复制粘贴实现。

我们同样需要让其他chromium分支能够方便地添加他们自己的特性,或者排除它们的构建以外的特性。

依赖管理概览

考虑这一点,让我们看一下依赖管理是如何工作的。我们有ProfileDependencyManager的一个单例,它与Profile创建与销毁相关联。一个PKSF由ProfileDependencyManager来注册以及注销。ProfileDependencyManager的工作是确保各个服务用一种安全的方式创建与销毁。

考虑下面这个有者三个服务工厂的例子:

AlphaServiceFactory::AlphaServiceFactory()
    : BrowserContextKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
}

BetaServiceFactory::BetaServiceFactory()
    : BrowserContextKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
  DependsOn(AlphaServiceFactory::GetInstance());
     }

GammaServiceFactory::GammaServiceFactory()
    : BrowserContextKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
  DependsOn(BetaServiceFactory::GetInstance());
     }

在这个简化的代码结构中,显式声明的依赖意味着这些服务唯一有效的创建顺序是[Alpha, Beta, Gamma],唯一有效的销毁顺序是[Gamma, Beta, Alpha]。上面的这些是你,也就是这个框架的使用者,所必须指定的依赖。

在幕后,ProfileDependencyManager管理所声明的依赖的关系,展示了一个Kahn的拓扑排序,并在CreateProfileServices()和DestroyProfileServices()中得到应用。

五分钟了解如何转换你的代码

  1. 让你已有的FooService继承BrowserContextKeyedService。
  2. 可能的话,不要再让你的FooService得到引用计数了。大多数与Profile相关的被引用计数的对象似乎因为他们没有使用base::bind/WeakPtrFactory,而需要在多线程使用自己的数据。(在这个例子里,线程安全的引用计数是有必要的,比如,多线程访问时,让你的工厂继承自RefcountedBrowserContextKeyedServiceFactory,这样一切都能正常工作。)
  3. 构建一个简单继承自BrowserContextKeyedServiceFactory的FooServiceFactory。消费者请求FooService时,你的FooServiceFactory将会是主要的访问点。
    1. BrowserContextKeyedService* BrowserContextKeyedServiceFactory::BuildServiceInstanceFor(content::BrowserContext* context)是唯一需要的函数。传入一个BrowserContext句柄,返回一个有效的FooService。
    2. 你可以用ServiceRedirectedInIncognito() 和 ServiceHasOwnInstanceInIncognito()控制incognito行为。
  4. 把你的服务添加到*chrome_browser_main_extra_parts_profiles.cc中中的EnsureBrowserContextKeyedServiceFactoriesBuilt()列表*。
  5. 理解Shutdown行为。出于历史原因,我们必须做两个阶段的Shutdown操作:
    1. 每个BrowserContextKeyedService首先要调用它的Shutdown()方法。使用这个方法来移除对Profile或其他服务对象的弱引用。
    2. 删除每个BrowserContextKeyedService,运行它的析构器。最小化的工作需要在这里完成。调用任何*ServiceFactory::GetForProfile()会在调试模式下触发的一个断言。
  6. 将每个”profile_->GetFooService()”实例改为”FooServiceFactory::GetForProfile(profile_)”。

如果你需要上面这些步骤的例子,可以看看这些补丁:

- r100516: 一个简单的例子,添加了一个新的ProfileKeyedService。这展示了一个最小的ServiceFactory子类。

- r104806: plugin_prefs_factory.h给出了一个例子,阐述了如何处理(必须)引用计数的东西。 这个补丁也展示了如何将你的首选项移到你的ProfileKeyedServiceFactory中。

调试技巧

使用依赖抽象器

Chrome有一个内置的方法来导出profile依赖图,生成一个GraphViz格式的文件。当你命令行运行chrome,附带–dump-browser-context-graph标记时,chrome会将依赖信息写到你的/path/to/profile/browser-context-dependencies.dot文件。然后你可以用dot转化这个文件,dot是GraphViz的一个部分:

dot -Tpng /path/to/profile/browser-context-dependencies.dot > png-file.png

这会给你一个像下面这样的抽象图(2012年1月23日生成,点击查看大图):

Shutdown时的crash

如果出现了一个这样的栈:

ProfileDependencyManager::AssertProfileWasntDestroyed()
ProfileKeyedServiceFactory::GetServiceForProfile()
MyServiceFactory::GetForProfile()
... [Probably a bunch of frames] ...
OtherService::~OtherService()
ProfileKeyedServiceFactory::ProfileDestroyed()
ProfileDependencyManager::DestroyProfileServices()
ProfileImpl::~ProfileImpl()

问题就是,OtherService没有正确地依赖MyService。在你使用Shutdown()组件时,框架会触发一个assert。

时间: 2024-08-16 07:20:35

【Chromium中文文档】Profile架构(看看谷歌家的重构)的相关文章

【Chromium中文文档】线程

线程 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Threading.html 全书地址 Chromium中文文档 for https://www.chromium.org/developers/design-documents 持续更新ing,欢迎star gitbook地址:https://ahangchen.gitbooks.io/chromium_doc_zh

【Chromium中文文档】安全浏览--Chrome中的警告都是怎么来的?

安全浏览 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/SafeBrowsing.html 全书地址 Chromium中文文档 for https://www.chromium.org/developers/design-documents 持续更新ing,欢迎star gitbook地址:https://ahangchen.gitbooks.io/chromium_d

Flutter 中文文档网站 flutter.cn 正式发布!

在通常的对 Flutter 介绍中,最耳熟能详的是下面四个特点: 精美 (Beautiful):充分的赋予和发挥设计师的创造力和想象力,让你真正掌控屏幕上的每一个像素. ** 极速 (Fast)**:基于 Skia 的硬件加速图形引擎,帮助你媲美原生应用的速度. 高效 (Productive):Flutter 的 Stateful Hot Reload (热重载) 特性帮助你实时看到应用修改的结果. 开放 (Open):不管是 Flutter 引擎还是 Dart 开发语言,甚至是工程团队的工作空

Visual Studio Code中文文档

Visual Studio Code中文文档 Visual Studio Code是一个轻量级但是十分强大的源代码编辑器,重要的是它在Windows, OS X 和Linux操作系统的桌面上均可运行.Visual Studio Code内置了对JavaScript, TypeScript和Node.js语言的支持,并且为其他语言如C++, C#, Python, PHP等提供了丰富的扩展库和运行时. 一.Visual Studio Code实际应用(一)快速强大的编码功能:    能够快速捕捉程

Laravel 5.1 中文文档

原文链接:http://laravelacademy.org/laravel-docs-5_1 由Laravel学院提供的Laravel 5.1 中文文档,供学习参考用,如有纰漏,请斧正: 序言 发行版本说明 升级指南 贡献代码 API文档 开始 安装及配置 Laravel Homestead 基础 HTTP 路由 HTTP 中间件 HTTP 控制器 HTTP 请求 HTTP 响应 视图 Blade 模板引擎 架构 一次请求的生命周期 应用目录结构 服务提供者 服务容器 契约(Contracts

SSH中文文档

SSH中文文档 SSH 一项创建在应用层和传输层基础上的安全协议,用于替代安全性差的TELNET,加密安全登陆用. SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议.利用SSH协议可以有效防止远程管理过程中的信息泄露问题.通过SSH可以对所有传输的数据进行加密,也能够防止DNS欺骗和IP欺骗. SSH之另一项优点为其传输的数据可以是经过压缩的,所以可以加快传输的速度.SSH有很多功能,它既可以代替Telnet,又可以为FTP.POP.甚至为PPP提供一个安全的"通道"

ENS中文文档系列之一 [ ENS介绍 ]

前言 ENS中文文档 是由我照ENS英文官方文档翻译而来,其中的一些内容和细节得到了ENS官方团队的指导.文档中包含 “LBB译注” 的地方是译者为了便于读者理解而进行的注释. 未来一段时间,我会在该博客定时发布ENS中文文档的一系列内容,对ENS域名或是区块链有兴趣的园友请关注本博客.为尊重汗水,我会在文前加上我翻译的原文链接(望管理员理解). ------------ ENS介绍 原文链接:https://liubenben.com/docs/readme.html ENS(Ethereum

Django REST framework 中文文档

参考链接: https://www.cnblogs.com/liwenzhou/p/8543035.html Django REST framework介绍 现在前后端分离的架构设计越来越流行,业界甚至出现了API优先的趋势. 显然API开发已经成为后端程序员的必备技能了,那作为Python程序员特别是把Django作为自己主要的开发框架的程序员, 我十分推荐Django REST framework(DRF)这个API框架. Django REST framework(DRF)框架文档齐全,社

Apache Storm 1.1.0 中文文档 | ApacheCN

前言  Apache Storm 是一个免费的,开源的,分布式的实时计算系统. 官方文档: http://storm.apache.org 中文文档: http://storm.apachecn.org ApacheCN 最近组织了翻译 Storm 1.1.0 中文文档 的活动,整体 翻译进度 为 96%. 感谢大家参与到该活动中来 感谢无私奉献的 贡献者,才有了这份 Storm 1.1.0 中文文档 感谢一路有你的陪伴,我们才可以做的更好,走的更快,走的更远,我们一直在努力 ... 网页地址: