应用开发的模式,无论是团队还是个人都在不断的发展,开源为软件行业提供了很多工具、框架、平台和操作系统,它们都越来越关注与灵活性和自动化,当今最流行的开源工具主要都集中在一些特性上,这些特性使得团队能够在开发和运维的各种级别上,不断地交付应用,较之以往,效率大为提升。
亚马逊的故事
从20世纪90年代初开始,一家总部位于西雅图的在线书店开始成为世界上最大的在线零售商,2015年,亚马逊超越沃尔玛成为美国最具价值的零售商,关于它无与伦比的增长故事中,最有趣的部分可以总结为一个简单的问题:作为一个简单的在线书店,一个网站如何转变成为世界上最大的零售商之一?
不难看出,在全球各地越来越多的企业介入互联网,使电子商务的世界发生了改变和重塑,随着个人电脑设备变得越来越小,智能手机、平板电脑,甚至智能手表无处不在,在分销渠道的可访问性方面经历了指数级增长,直接改变了世界商业的方式。
亚马逊首席技术官Werner Vogels见证了亚马逊从一家非常成功的在线书店到世界上最具价值的科技公司和产品零售商的这种技术衍变。2006年6月Vogels接受了计算机杂志ACM的采访:这是一项技术选择的快速发展,这一技术为亚马逊的增长提供了动力,在采访中,Vogles谈到了背后的核心驱动力,在亚马逊的技术发展中,有很大一部分是为了实现这一持续的增长,在保持可用性和性能的同时,实现了超可扩展性。
对于亚马逊来说,要实现这一能力,需要转向不同的应用架构模式,其提到亚马逊是一个单一的应用,随着时间的推移,越来越多的团队在同一个应用上运行,代码库所有权的界限开始变得模糊,Vogels说:没有孤立的情况,因此也就没有明确的所有权。
Vogels指出,像数据库这样的共享资源,使其很难扩展整个业务,共享资源的数量越多,无论是应用服务器还是数据库,当将特性交付到生产环境时,控制团队就越少。
他触及了一个共同的主题:云应用共享,团队拥有他所构建东西的想法,他说:传统的模式是,将应用开发出来,然后又将开发运维分离,开发将应用抛给运维就撒手不管了。
在一些全球最重要的软件会议上,一些著名的演讲嘉宾都引用了一句最常用的名言——You build it,you run it。这句话后来成为了我们今天所熟知的DevOps口号。
Vogels在2006年谈到的许多实践都是当今流行概念的影子,如DevOps和微服务这样的实践。尽管类似于亚马逊这种大型互联网公司正在开发类似的应用,但,围绕这些想法的工具需要数年的时间才能开发并成熟为一种服务。
2006年,亚马逊推出了一款名为Amazon Web Service(AWS)的新产品,其理念是提供一个与亚马逊内部使用的平台相同的平台,并将其作为服务发布给公众,亚马逊迫切希望看到这个平台背后的理念和工具的商业化,Vogels推出的许多想法都已经在亚马逊的平台上建立了,通过该平台发布为公众服务,亚马逊将进入一个名为“公有云”的新市场。
公有云背后的逻辑是合理的,虚拟资源可以按需供应,而无需担心底层基础设施,开发者可以简单地租用虚拟机来存放他们的应用,无需购买或管理基础设施,这是一种低风险的自助服务,它将有助于提供公有云的吸引力,而AWS一直在采用方面保持着领先。
AWS将需要数年时间才能形成一套用于构建和运行应用的服务以及模式,这些应用都是在公有云上运行的,虽然许多开发人员都涌向这些服务去构建新的应用,但对于很多公司来说,迁移是一大难题,因为现有的应用不是为了可移植性而设计的,此外许多应用仍然依赖于与公有云不兼容的遗留工作负载。
为了让大多数公司能够利用公有云,他们需要改变开发应用的方式。
平台
平台,是时下常被提及的一个词。
当讨论计算的平台时,通常讨论的是一组帮助构建或运行应用的能力,平台最能概括的是,它们对开发者如何构建应用施加了约束。
平台能够自动完成对应用的业务需求不十分重要的那些任务,这使得开发团队更加灵活,因为他们只支持那些有助于区分业务价值的特性。
任何编写过Shell脚本或已标记的容器和虚拟机来实现自动化部署的团队都已经构建了一个平台,问题是:这个平台能保证什么?为持续交付新应用所需的大多数(甚至全部)的需求需要做多少工作?
当构建平台时,其实正在构建一个工具,可以自动化一组可重复的实践。实践是由一组将有价值的想法转化为计划的约束所制定的。
想法:平台的核心理念是什么?为什么它们具有价值?
约束:将平台的想法转化为实践的比较条件是?
实践:将约束自动化到一组可重复的实践中?
每个平台的核心都是简单的想法,当实现时,通过使用自动化工具来增加不同的业务价值。
以亚马逊平台为例,Vogels说,通过增加应用组件之间的隔离,团队可以对他们的交付特性进行更多的控制。
想法:
- 通过增加应用组件之间的隔离,能够快速且独立地交付系统的一部分。
通过将这个想法作为平台的基础,我们可以将其变成一组约束,约束以一种观点的形式出现,关于核心理念是如何在自动化的实践中创造价值的,下面的语句是关于如何提高组件隔离性的一些限制。
约束:
- 应用组件将作为独立的可部署服务构建。
- 服务中所有业务逻辑都封装在它所操作的数据中。
- 从服务外部无法直接访问数据库。
- 服务将发布一个Web接口,该接口允许从其他服务访问其业务逻辑。
有了这些限制,对于如何在实践中增加应用组件的隔离性有了一定的认知,当自动化到实践时,这些约束的Promise将为团队提供更多的控制,以控制特性是如何交付的生产的?下一步是描述如何将这些约束捕获到一组可重复的实践中。
从这些约束中派生出来的实践应该作为Promise的集合来声明,通过将实践表述为Promise,我们对平台的用户保持了对他们如何构建和操作应用的预期。
实践:
- 向团队提供了一个自助服务接口,允许提供操作应用所需的基础设施。
- 数据库以服务的形式提供给应用程序,并使用自助服务接口进行供应。
- 应用是作为环境变量提供给数据库的凭证,但前提是在声明与数据库的明确关系作为服务绑定之后。
- 每个应用都提供了一个服务注册表,该服务注册表被用作在何处定位外部服务依赖项的清单。
上面列出的每个实践都以向用户Promise的形式呈现,这样,平台核心思想的意图就会被用于应用的约束来实现。
云原生应用是建立在一组约束的基础上的,这些约束可以减少在进行无差异的繁重工作时所花费的时间。
AWS首次向公众发布时,亚马逊不强迫用户遵循相同的约束,他们在内部用于Amazon.com.Staying Amazon Web服务名称,AWS本身不是一个云平台,而是独立的基础设施服务的集合,可以组合成自动化工具类似于一个平台的Promise,在AWS的第一次发布后的几年里,亚马逊开始提供一系列管理平台服务,从物联网到机器学习。
如果每个公司都需要从头开始构建自己的平台,那么在应用程序中交付价值的时间就会被延迟,直到平台完全组装好,早期采用AWS的公司需要将一些类似平台的自动化设备组装在一起,每家公司都必须在一系列的Promise中进行,这些Promise抓住了如何开发和交付应用的核心理念。
最近,每个云平台都应该有一套基本且共同的Promise,通过使用开源平台作为服务(PaaS)云计算,这些Promise将在本文中得到探索,云计算背后的核心动机是提供一个平台,它封装了一套用于快速构建和操作的通用Promise,云计算公司做出了这些Promise,同时还提供了许多个不同的云基础设施提供商之间的可移植性。
这本书的主题是如何构建云原生Java应用,我们将主要关注工具和框架,通过利用云平台的有点和Promise,帮助减少未分化的繁重工作。
模式
如何运用新模式去开发应用,会使人更深入地思考应用在生产中的行为,开发和运维都更加强调他们的应用如何在生产中运行,在失败的情况下更少的保证复杂性将如何破解。
与Amazon.com的情况一样,应用开始远离大型的且单一的模式,现在架构的重点是在不牺牲性能和可用性的情况下实现超可伸缩性,通过分解一个庞然大物的组成部分,工程组织正在努力分散变更管理,为团队提供更多的控制,使它们能够更好地控制产品的生产方式,通过增加组件之间的隔离,开发团队开始进入分布式系统开发的世界,重点是构建更小、更专注的服务,并具有独立的发布周期。
云原生利用了这一组模式,让团队在向生产交付特性时更加敏捷,随着应用的分布越来越分散(为了向拥有应用的团队提供更多的控制所必须的隔离的结果。)在应用组件通信方式中失败的集成会成为了一个重要的关注点,随着应用程序转变为复杂的分布式系统,操作失败是不可避免的。
云原生架构提供了超可伸缩性的好处,同时仍然保证了应用的总体可用性和性能,尽管如亚马逊这样的公司在云计算中获得了超可伸缩性的好处,但广泛使用的用于构建云原生应用的工具尚未浮出水面,这个工具和平台最终将作为一个由公有云早期开拓者维护的开源项目的集合而被开发出来:Netflix。
可靠性
团队之间(开发、运维、测试等)自荐建立的期望是契约,这意味着提供或使用某种级别的服务,通过观察团队在开发应用过程中如何为彼此提供服务,我们可以更好地理解通信中的失败是如何导致风险从而失败的。
创建团队之间的服务协议是为了减少为业务带来价值的整体功能意外行为的风险,团队之间的服务协议是要明确的,以保证行为与预期的运营成本一致,通过这种方式,服务可以使业务单元最大化其总输出,应用业务的目标是通过成本——我们称之为可靠性的成本去预测价值的创造。
业务的服务模型与构建应用时使用的模型相同,这就是如何保证系统的可靠性,无论是在生产应用中自动化功能,还是在培训的人员执行手工操作的情况下。
敏捷性
因为不再是只有一种开发和运维应用的方法,在采用敏捷方法和将应用作为服务(SaaS)业务模型的推动下,惬意应用堆栈正变得越来越分布式,开发分布式系统是一项复杂的任务,向公司提供更分布式的应用架构的做法,是由于需要更快地交付应用,并且减少了失败的风险。
注意项
最近有一些人在盛传敏捷已死这种理念,但正如我们所说,敏捷其实指的是一种以组织为单位的热情来传递新的价值以及快速响应的想法,条条大路通罗马,本文不关心你所信奉的管理实践,关键是要明白敏捷是一种价值,而不是目标。
现代应用定义业务寻求重组其开发过程,以使应用项目更快地交付,并将应用持续地部署到生产中,不仅是公司想要提高应用的开发速度,而且还要增加他们创建和运维应用的数量,以服务于一个组织的各种业务单位。
应用正日益成为企业的竞争优势,更快和更好的工具能让业务专家打开新的收入来源,或者以促进快速创新的方法优化业务功能。这场运动的核心是云计算,当谈论云计算时,我们讨论的是一组非常具体的技术,这些技术使开发和运维能够利用现有的Web服务来提供和管理虚拟计算基础设施。目前的企业正开始走出数据中心,进入公有云阶段,Netflx,正式一家流行的基于订阅的流媒体公司。
Netflix的故事
目前,Netflix是世界上最大的按需流媒体服务公司之一,在云服务中运营期在线服务,Netflix于1997年在加州由Reed Hastings 和 Marc Randolph创立,最开始Netflix提供了一种在线DVD租赁服务,允许用户每月付费订阅无限制的电影,而不收取其他任何费用。
2008年,Netflix经历了一场严重的数据库故障,使得该公司无法将任何DVD邮寄给客户,当时,Netflix刚刚开始向客户提供流媒体视频服务,其流媒体团队意识到,类似的服务中断将对其未来业务造成毁灭性的打击,因此,Netflix做出了一个至关重要的决定:采取一种与众不同的方式开发和运行应用,确保客户永远都是使用服务。
因此它们决定放弃垂直规模的基础设施和单一的失败点,实现是数据库损坏的结果,这是使用锤石伸缩的关系数据库结果,Netflix将其客户数据迁移到一个分布式的NoSQL数据库,是一个名为Apache Cassandra的开源数据库项目。这也被视为“云原生”公司的开始,将所有的应用作为高度分布式和弹性的服务在云中运行,Netflix公司通过向其应用和数据库中添加冗余来增加其在线服务的健壮性,并在一个扩展的基础设施模型中增加了冗余。
作为迁移到云端的一部分,Netflix决定将其庞大的应用部署迁移到高度可靠的分布式系统上,但这面临着一个重大的挑战:Netflix的团队将不得不重新架构应用,同时从一个内部数据中心转移到公有云,2009年,Netflix开始向Amazon Web Service(AWS)迁移,并专注于三个主要的目标:可伸缩性、性能、和可用性。
很明显,2009年初的需求将大幅度增加,事实上,Netflix的云计算和平台工程副总裁ury Izrailevsky在2013年的AWS大会上表示,自2009年依赖,该公司已经增加了100倍。
此外,他还指出,当考虑到期快速的全球扩张时,可伸缩性在云计算中的优势变得更加明显“为了给我们的欧洲客户提供更好的低延迟体验,我们在爱尔兰推出了第二个云计算区,在不同的地区建立一个新的数据中心需要花费数月的时间和数百万美元的资金,这是一个巨大的投资。”
随着Netflix开始在Amazon Web Service上托管应用,员工们在Netflix公司的博客上记录了他们的学习经验,Netflix的许多员工都在提倡一种新的架构,这种架构专注于软件栈的各个层面的水平可扩展性。
John Ciancutii当时是Netflix的个性化技术副总裁,他在2010年的公司博客上写道:云环境是水平扩展机构的理想选择,我们不必猜测未来几个月我们的硬件、存储和网络需求将会是什么样子的,可以通过编程方式从Amazon Web Service中的共享池中访问更多资源。
Ciancutti所说的“以编程方式访问”资源的意思是,开发和运维可以通过编程的方式访问由Amazon Web Service公开的某些管理API,以给客户提供一个用于提供他们的虚拟计算基础设施的控制器,RESTful API为开发人员提供了一种构建管理和为其应用提供虚拟基础设施的应用的方法:
Ps:为控制虚拟计算基础设施提供管理服务是云计算的主要概念之一,称为基础设施即服务,通常称为IaaS。
云计算堆栈
Ciancutti在同一片博客文章中坦白,Netflix并不商场预测客户的增长或设备的参与,这是云原生企业背后的一个核心主题,云原生是一种心态,它承认无法可靠地预测何时何地需要容量。
在Yury Izrailevsky 2013年的演讲中,他说:在云计算中,随着流量的增加,我们可以在几天内提高产能,当我们成为一家全球性公司的时候,我们可以依靠在世界各地的多个亚马逊网络服务区,无论他们在哪里,都能给我们的客户提供一个很好的互动体验。”
受益于AWS国际扩张的规模经济也让Netflix受益,随着AWS向美国以外地区扩展了可用性区域,Netflix只使用AWS提供的管理API扩大了气服务范围。
Izrailevsky引用了企业云计算的普遍观点:“当然,云计算非常好,但对于我们来说太贵了。”他对这一观点的回应是:“Netflix迁移到云,运维成本下降了87%,所支付的费用,是之前数据中心支付的八分之一。”
Izrailevsky进一步解释了为什么云计算为Netflix提供了如此巨大的成本节省:在不担心容量缓冲的情况下,能够实现增长是很有帮助的。随着成长,又可以扩大需求。
微服务
云原生应用和微服务是齐头并进的,构建微服务的主要思想之一是让功能团队围绕特定的业务功能组织自己和应用,这种方法无论如何都不是特别新颖,从小型的分布式组件中创建一个系统,这些组件可以很好地工作,并以这样的方式拆分,以尽可能减少将单个特性交付到生产环境中的阻力,这几十年来一直是可能的,那么,为什么这种构建模式现在才流行起来?这和云有什么关系吗?
构建应用之一都很困难,理智与行为之间的区别往往是别人剥夺了你自己糟糕的选择结果,昨天做出的技术决策将会妨碍你明天做出正确决策的能力。
理解一件小事很容易,但要理解它的缺失所带来的影响则要困难得多,如果我们仔细研究一个设计良好的单片应用,我们将看到类似的驱动,如果我们在今天的微服务架构中看到的模块化、简单性和松耦合,当然,其中一个主要的区别是历史,不难理解,选择层如何将精心设计的东西变成了一个糟糕的东西。如果一个人在架构的一个可替换单元中做出了糟糕的选择,那么这个选择就会随着时间的推移而变得更容易分解,但是,如果同一个人在设计一个精心准备的庞然大物的多个独立模式上工作,那么额外的历史可能会影响到其他人以后做出理想选择的能力,因此,最终不得不妥协,被迫在一些从未有机会做出的选择上做出稍微更好的决定,而这些选择是我们从未有过的。
想要改变的应用就变成了一种生活的东西——总是被历史所改变,永远不会对生存之风的变化免疫,正因如此,必须以改变为基础,必须拥抱变革,同时抵制未来构建的诱惑,毕竟,未来的设计知识一种华丽的设计,把它装扮成敏捷的开发,无论我们多么聪明,在预先设计完美的系统时,都无法准确地预测它的功能在未来会发生怎样的变化,因为通常情况下,它不会由我们来决定,市场倾向于决定产品的命运,正因如此,只能在头脑中进行设计。
微服务只是一个想法,模式和实践在一个流体状态下仍然波动,而耐心地等待一个稳定的定义,他们在更广泛的垂直领域的拥抱——更好还是更糟——还有待最终裁决。
有两种主要的力量影响着架构变化的速度:微服务和云计算,云计算极大地降低了管理基础设施所需的成本和工作量,目前,我们能够使用自助服务工具为应用程序提供基础设施,由此,新的、创新的开始迅速地成为现实,使我们重新思考和重塑以前的管理,之前关于构建应用的真实情况可能是不真实的,在大多数情况下,真相被是被掩盖的,现在我们发现自己需要在反复无常的假设基础上做出艰难的决定:服务器是物理的,而虚拟机是永久的,容器是无状态的。
Splitting the Monolith
Netflix列举了从一个单一的庞然大物中迁移到分布式架构的两个主要好处:敏捷性和好处。
在使用云之前,Netflix的架构包括一个单一的Java虚拟机(JVM)应用,虽然拥有一个大型的应用程序部署有很多优点,但主要的缺点在于开发团队由于需要协调变更而减缓效率。
当构建和运维应用时,集中化降低了需求协调的成本增加的风险,协调需要时间,应用体系构建越集中,就月需要花费更多的时间去协调对其中任何一项的更改。
一体化也不太可靠,当组件在相同的虚拟机上共享资源时,一个组件中的故障可能会蔓延到其他组件,从而导致用户宕机,当团队需要协调他们的变更时,在一个单一的块中做出一个变更的风险会增加,在单个发布周期中发生的变化越多,就越有可能造成宕机,通过将一个单一的块分割成更小、更专注的服务,可以在团队的独立发布周期中使用较小的批处理大小来进行部署。
Netflix不仅需要改变其构建和运营应用的方式,还需要改变其组织文化,Netflix转向了一种名为DevOps新的操作模式,在这个操作重视中,每个团队都变成了一个产品组,远离了传统的项目组结构,在一个产品中,团队是垂直的,在每个团队中嵌入运维和产品管理,产品团队会拥有他们所需的一切构建和运维应用。
Netflix OSS
随着Netflix转型为一家云计算公司,它也开始积极参与开源项目,2010年末,时任系统与电子商务工程副总裁的Kevin McEntee在一篇文章里所:该公司未来将在开源领域发挥作用。他说,一个号的开源项目能够解决一个共同的挑战,那就是它发展了自己的动力,并且在持续改进的良性循环中持续很长一段时间。
在这一宣布之后的几年间,Netflix开放了超过50个内部项目,每个项目都成为了Netflix战略品牌的一部分。
2012年7月,Netflix的云平台工程主管Ruslan Meshenberg在公司的技术博客上发表了一篇文章,这篇博客的文章:《Netflix的开源》解释了为什么Netflix才去了如此大胆的举措,开源了如此多的内部工具。
Meshenberg在博客中写道:“Netflix是一个早期的云采用者,将我们所有的流媒体服务都在AWS的基础设施上运行”。
Netflix为开源社区和技术生态系统做出的文化动机被认为与微观经济概念背后的原则紧密相关,即规模经济,在被称为“云时代”到来之际,Netflix发现它的先去不是如IBM或微软这样的科技公司,而是诞生在互联网背后的公司,Netflix和亚马逊都是始于90年代末的网络公司,都是通过提供旨在与实体公司竞争的在线服务而起家。
目前,Netflix和亚马逊都已经大大超过了同类型实体店的估值,随着亚马逊进入云计算市场,它将自己的集体经验和内部工具转变为一套服务,Netflix随后也在亚马逊的服务上做了同样的工作,在此过程中,Netflix开放了自己的经验以及工具,转型为一家基于亚马逊AWS提供的虚拟基础设施服务的云原生公司,这就是规模经济推动了云计算行业革命。
2015年初,据报道Netflix的第一季度财报显示,该公司的估值达到了329亿美元。
云原生Java
Netflix向软件行业提供了丰富的只是,这是它成为云原生公司的结果,本文借鉴Netflix的经验和开源项目,并将其作为一套模式,其中有两个主题:
使用Spring和Netflix操作系统构建具有弹性的分布式系统
使用云计算,持续交付来操作本地云原生应用
以及云原生的12要素,12要素的方法是由Heroku云平台的创建者编写的一套流行的应用开发原则。
云原生12要素的核心思想:
为安装自动化使用声明式格式,以最小化新开发人员加入项目的时间和成本
与底层操作系统有一个明确的契约,在执行环境之间提供最大的可移植性
适用于现代云平台上的部署,从而避免了服务器和系统管理的需求
最小化开发和生产之间的差异,从而实现最大敏捷性的持续部署
并且可以在没有对工具、架构或开发实践进行重大更改的情况下进行扩展
云原生的12要素可以帮助构建利用这些想法的应用,它是一套基本的约束,可以用来构建云原生应用,由于这些因素涵盖了所有现代云平台的常见实践,因此构建12要素应用是云本地应用开发的一个常见起点。
图片摘自网络
上图是12条因素即12条军规,这里挑几个重要进行讲解:
Codebase:和整个测试环境有关,大家拉基线是为了整个版本的稳定性。
依赖:要解决依赖的问题,若用Java的话,意义不大,原始上会有依赖管理,但电商公司有各种语言都非常原始,直接依赖于源代码,若其版本发生变化,有些API就编译不过去。
Configuration:Java和其他语言非常冲突就在于此,做Java的同学都知道配置一般都会打在根应用的生产日报里,会有大量的配置文件,上到云应用,让底层人员运行作业炸包,首先就要配置文件,一切配置要么走配置中心,连访问配置中心的地址可能也是外部注入进来,不用再去配置上声明整个中心是什么;要么是所有的配置都由平台帮助注入,不能自己携带相关的Jap去做,原来整个构建,底下会有一些构建的模式,之前构建最早版本非常有意思,比如大了一个测试环境的炸包,若没问题,即可交给运维,因为配置VI里面完全不一样,会在应用和部署的公共机器时,有自动替换配置文件的动作,要替换的配置文件其实也是预先上传到平台上的,整个发布平台会帮助做一个配置文件的替换,方式很原始,不是运行它去改配置,因为包本质上是变化的,配置文件属于包的一部分,因此它也是变动的,等于整个包的密封性被打破了,此时去上一个云原生的平台,首先要做的是灭配置文件,要通过环境变量或启动平参数的模式去启动应用,这时平台能自动地把整个环境——生产、预发布、测试、以及延长等环境,将不同的配置设置好,所以这一点对开发来说改动比较大,正常上这种云应用,最难的是将配置问题解决,因为大量的Java配置都是在文件中进行,包括内部有框架的,特定的文件,将这些都清除掉。
Backing Services:即不要把依赖服务完全写死,依赖服务也是通过环境注入进来,如数据库连接,可以通过外部配置进来。
构建、发布和运行:要流程化。
进程:进程是微服务的根基,应用应以进程的级别运行,跟原来的方式不同,很多功能都是达到一个进程,通过不同的线程运行,但有几个不好的地方,类似于微服务,发布可能会影响不应影响的一些部分,追踪也不是很好做,比如当当最近在做内部的Tracing,目前只能做到进程间的Tracing,如果内部的线程Tracing需要改造,还是有一些麻烦的。
端口:类似于一种端口的绑定解决方案,是由编排工具动态注入,要动态监听一些要发布的端口信息。
日志:不应生成文件,而是通过服务的方式将其进行传递,整个管理平台应有自动收集日志的功能,这也达到了云原生的态度,要将所要定位的信息不和应用绑在一起,因为应用很快会启动或注销,那么整个轨迹要持久化的保留。
(节选自高洪涛老师的《12条军规说Dev,3大重点讲Ops——当当网的云原生之路》)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。