看到架构了吗?我看不到,但她是

在今天hh.ru的发展中,大约有150人。我们有很多有趣的团队,每个团队都做出了重要贡献。但是在本文中,我只会讲其中之一。


因为我是她的团队负责人,所以有以下几个原因:

  • 候选人常常不了解我们在做什么;
  • 有时甚至公司内部的员工也不知道这一点,因为我们的团队没有产品经理,自己的业务职能区域以及我们支持的服务清单...;
  • 我们的功绩最经常隐藏在阴影中;
  • 最后,“如果您想弄清楚,请尝试向某人解释” :)

因此,我将尝试通过易于理解的示例来理解我们的工作实际上包含的内容。

让我们从最通用的优先级开始,即优先级有两个:

  • 系统支持我们平台的可靠性和弹性。这里值得一提的是“系统”一词-这意味着我们不会修复特定的性能缺陷,而是会开发通用的规则和模式,将其修复在框架中,自动检查等,以便它对每个人都有效。
  • 开发侧重于业务逻辑。也就是说,开发人员对支持可靠性,体系结构等的考虑较少。-更好。显然,完全消除同事的这种想法是有害的,但保持合理的平衡是有道理的。

从这些优先事项出发,我们的主要工作方向如下:

0.架构的支持与发展


hh.ru是用户的5-6k rps,高峰时达到10k,并以一个数量级增长,到达后端。这是1,500多个实例,在3个DC中旋转了约150个服务。所以是的,首先,这些是非常多分支的方案,带有正方形,银行和箭头:谁去哪里,应该在哪里。当然,我们不会制定方案-我们会通过自动化,日志记录和监视来满足需求,但是例如,我们会吓到我们的学生



我们真正负责查找和消除体系结构中的瓶颈和不灵活的解决方案,并根据需要进行开发。

我举一个例子:

hh.ru距第一年的工作已经很久了,一旦有一个单独的计算机按时间表执行后台任务似乎是一个好主意-您可以为其分配更多的资源,并且没有任何竞争。但是我们到底有什么呢?

  • 所有任务的失败点
  • 唯一的配置仅在产品中复制
  • 逻辑设计为在单独的粗体计算机上专用启动且不会水平缩放的任务

当我们理解了这一点之后,我们确定了我们有足够的能力将王冠任务转移到一般实例上,并且在技术债务类别中开始了一项艰巨的任务-现在是时候偿还债务了,同事们正在逐步消除这个问题。

1. 拐杖的标准化


首先,这是我们对服务的快速发展框架和工具:螺母和螺栓frontik。无论如何,在我们的github上打开的jclient和许多其他库都是从这样的思想中产生的:聚合操作各种技术的经验是有意义的。这使我们能够培养自己在战斗中遇到的局限性,设计模式和行为方式,我们认为它们是最合适,最易懂和最可靠的。 除了这些显而易见的标准化示例外,还有一些合理的方法可以概括特定的解决方案。



例如,在某个时候,我们开始定期需要将消息发送到rabbitmq(至少一次)。基地的自写队列反复地解决了这些任务,而dba一遍又一遍地说,基地的队列受到了强烈的爱戴,尤其是那些装满的队列。最后,很明显,这里需要一个标准解决方案,对于dba来说是可接受的,以确保可靠的交付并便于开发-这就是我们编写用于集成pgq和Rabbitmq的库的方式。现在很有可能我们也将pgq与kafka一起使用。

1.0。虫子


错误也是全球性的。例如,在某个时候,我们发现在每个流程工作人员的领事中都注册了我们的python框架,甚至在应用程序准备好接受请求之前就这样做了。在框架中修复后,更改将逐渐随着更新而扩展到所有服务。

我在演示阶段jpoint 2019讨论了与jvm设置有关的另一个常规错误

举例来说,如何处理每周一次在其中一个实例上重现的错误,并通过重新启动来进行处理,但无论是负载还是合成都不会重现该错误?
, java- . nuts-and-bolts:

"qtp1778300121-22" #22 prio=5 os_prio=0 cpu=797.67ms elapsed=11737.06s tid=0x00007f5890139000 nid=0x26 waiting for monitor entry [0x00007f58922c7000]
java.lang.Thread.State: BLOCKED (on object monitor)
at ch.qos.logback.core.AppenderBase.doAppend(AppenderBase.java:63)
- waiting to lock <0x00000000e86acad0> (a ru.hh.nab.logging.HhSyslogAppender)
at ru.hh.nab.logging.HhMultiAppender.doAppend(HhMultiAppender.java:47)
at ru.hh.nab.logging.HhMultiAppender.doAppend(HhMultiAppender.java:21)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)


:

"qtp1778300121-22" #22 prio=5 os_prio=0 cpu=5718.81ms elapsed=7767.14s tid=0x00007f1537dba000 nid=0x24 waiting for monitor entry [0x00007f153d2b9000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.4/ConcurrentHashMap.java:1723)
- waiting to lock <0x00000000e976a668> (a java.util.concurrent.ConcurrentHashMap$Node)
at org.springframework.beans.factory.BeanFactoryUtils.transformedBeanName(BeanFactoryUtils.java:86)


jackson:

"qtp1778300121-23" #23 prio=5 os_prio=0 cpu=494.19ms elapsed=7234.32s tid=0x00007f6c01218800 nid=0x25 waiting for monitor entry [0x00007f6c07cfa000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase._endpointForWriting(ProviderBase.java:711)
- waiting to lock <0x00000000e9f94c38> (a org.glassfish.jersey.jackson.internal.jackson.jaxrs.util.LRUMap)
at org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase.writeTo(ProviderBase.java:588)


Code Cache:



java . java, , - . , .

1.1。一般决定


有时,在此成为严重问题之前,可能会提出标准解决方案。作为示例,我们可以引用Vlad Senin 在同一jpoint 2019上讨论的日志处理任务或我们的http客户端中的超时管理任务。

其含义是,确定合理的超时时间(而不是在客户端,而是在服务器端)非常有用。对于服务器,我们拥有有关其对端点的响应速度的数据。现在,我们的客户支持该服务的一次超时。但是很明显,并非所有服务端点都以相同的方式响应-有些响应时间更长,有些响应速度更快。我希望能够使用不同的超时时间。否则,将发生类似于以下情况:



到目前为止,这种情况仅在压力测试下才会出现,但是我想在这成为问题之前解决它们。

1.2。开放式问题


但是,并非所有问题都由一些重要的地方和复杂的手动过程来解释。此外,我将给出一些例子,这些例子也属于我们的优先领域,但与此同时,确定性也要差得多。因此,我将仅描述初始数据,如果需要,我们可以在评论中讨论解决方案。

因此,第一个示例:现在很明显,存在将我们的服务集成到彼此之间的问题。例如,将站点句柄集成到API中可能要比其最初的开发花费更长的时间。

另一个可能是许多类似问题的例子,就是看到一个整体。每个人都知道,一块堆满了大量遗留物的巨石使开发和操作变得复杂。但是谁能告诉我多少呢?值得牺牲技术债务的其他任务而转而进行锯割,而每个锯齿分别承担很小的价值吗?

这些和类似问题的规模如此之大,以至于要解决这些问题,有时必须远远超出技术框架,而要涉足工作流程的全新领域。一方面,这令人恐惧,但另一方面,在选择决策时提供了令人难以置信的自由。

2.我们的工作方式


如果没有描述我们如何处理所有这些工作,那么关于我们工作方向的故事将是不完整的。

首先,是什么吸引了我进入“建筑”领域,又激发了我们所有人的动力:我们真正为质量而努力。

在石头向我扑来之前,我会尽力解释我的意思。我相信没有开发商故意在质量上得分。关键在于技术债务:如果我们谈论的是一部分计划不被重用的业务逻辑,那么一个不太理想的解决方案的债务数量很可能会随着时间的推移而缓慢增长(如果有的话)。

这使您可以稍微放松自己的完美主义-开始承担债务的任务,然后进行下一次迭代。但是,如果我们谈论的是在数百个应用程序中使用的框架或全局配置准备工具,并整合了某些设计或命名模式,那么因决策失败而导致的债务增长率会完全阻止任何收益。显然,在某些情况下,即使最好的解决方案在使用时也会显示出缺点,但是这种情况并不经常发生……

接近完成时,我想谈一谈我们仍然遇到的障碍。没有这个,关于我们工作的故事将是不诚实的。所以。

2.0。难度评估任务


如上所述,我们无法评估所有任务的有益效果。当“盒装”解决方案用于某些功能时,任务释放时间将减少多少?应该首先重构两个有问题的代码部分中的哪一部分?为了开发适当的评估任务的系统,我们每周开会几次,持续了几个月,但这是一个单独的主题。

2.1。集体无意识


协调150人的工作并非易事。我们非常分散的组织结构通常会以其最好的一面表现出来,但是对于“架构”而言,有时这是一个严重的障碍。几乎不可能达成协议的协议,甚至更少可以监视其遵从性的协议。

并且所有变更必须顺畅滚动。该服务可能要几个月后才能更新,但是仍然存在一个整体。

所以我们聊了


我希望在我讲完故事后,我在hh.ru中澄清了“架构”的含义。而且,如果我设法引起您对我们工作的兴趣,那通常很棒。而且,我们的团队现在有空缺我们将为新的想法感到高兴,这些新的想法将帮助我们实现从闲置的目光中隐藏起来但如此重要的胜利。

ps 原来,KDPV就是这个人的例证我希望他不反对将其图像用作KDPV

All Articles