Voir l'architecture? Et je ne vois pas, mais elle est

Dans le développement de hh.ru aujourd'hui environ 150 personnes. Nous avons de nombreuses équipes intéressantes et chacune apporte une contribution significative. Mais dans cet article, je ne parlerai que de l'un d'eux.


Parce que je suis son chef d'équipe. Et cela pour plusieurs raisons:

  • souvent, les candidats ne comprennent pas ce que nous faisons;
  • parfois même les employés de l'entreprise ne le savent pas, parce que notre équipe n'a pas de chef de produit, son propre domaine fonctionnel et la liste des services pris en charge par nous ...;
  • nos mérites restent le plus souvent à l'ombre;
  • à la fin, "si vous voulez le comprendre, essayez de l'expliquer à quelqu'un" :)

Par conséquent, je vais essayer de comprendre avec des exemples compréhensibles en quoi consiste notre travail.

Commençons par le plus général, c'est-à-dire avec les priorités, il y en a deux:

  • support système pour la fiabilité et la résilience de notre plateforme. Ici, il convient de prêter attention au mot «système» - cela signifie que nous ne corrigerons pas des défauts de performances spécifiques, mais développerons des règles et des modèles généraux, les corrigerons dans des cadres, des vérifications automatiques, etc. afin qu'il fonctionne pour tout le monde.
  • développement axé sur la logique métier. Autrement dit, moins un développeur pense à prendre en charge la fiabilité, l'architecture, etc. - Tout le meilleur. Il est clair que débarrasser complètement ses collègues de telles pensées est plutôt nocif, mais maintenir un équilibre raisonnable est logique.

De ces priorités découlent les principales directions de notre travail:

0. Support et développement de l'architecture


hh.ru est à 5-6k rps des utilisateurs, au sommet atteignant 10k, qui augmentent d'un ordre de grandeur, atteignant les backends. Cela représente plus de 1 500 instances, faisant tourner environ 150 services dans 3 DC. Alors oui, tout d'abord, ce sont les schémas très branchés avec des carrés, des banques et des flèches: qui va où, où devrait être. Bien sûr, nous ne dessinons pas de schémas - nous couvrons les besoins avec l'automatisation, la journalisation et la surveillance, mais nous avons effrayé nos étudiants , par exemple, avec de telles choses:



nous sommes vraiment responsables de trouver et d'éliminer les goulots d'étranglement et les solutions inflexibles dans l'architecture et de les développer en fonction des besoins.

Je vais donner un exemple:

hh.ru a travaillé loin de la première année, et une fois que cela semblait être une bonne idée d'avoir une machine séparée pour effectuer des tâches d'arrière-plan selon un calendrier - vous pouvez allouer plus de ressources pour cela et il n'y aura pas de courses dessus. Mais qu'avons-nous finalement:

  • point d'échec pour toutes les tâches
  • configuration unique reproduite uniquement dans prod
  • tâches dont la logique est conçue pour un lancement dédié sur une machine en gras distincte et ne se met pas à l'échelle horizontalement

Lorsque nous avons compris cela, nous nous sommes assurés que nous avions tous les moyens de transférer les tâches de la Couronne aux instances générales, et nous avons commencé une grosse tâche dans la catégorie de la dette technique - maintenant que le moment est venu de rembourser les dettes, les collègues éliminent progressivement ce problème.

1. Normalisation des béquilles


Tout d'abord, ce sont nos cadres et outils pour le développement rapide des services: écrous et boulons et frontik . Quoi qu'il en soit, jclient et de nombreuses autres bibliothèques ouvertes sur notre github ont émergé de l'idée qu'il est logique d'agréger l'expérience de l'exploitation de diverses technologies. Cela nous permet de cultiver les limites, les modèles de conception et de comportement que nous avons élaborés au combat, et nous les considérons comme les plus appropriés, compréhensibles et fiables.

En plus de ces exemples évidents de normalisation, il y a ceux dans lesquels il est logique de généraliser des solutions particulières.

Par exemple, à un moment donné, nous avons commencé à avoir périodiquement besoin d'envoyer des messages à rabbitmq (au moins une fois). Les tâches ont été résolues à plusieurs reprises par des files d'attente auto-écrites à la base, et dba a dit à maintes reprises combien les files d'attente à la base étaient FORTEMENT aimées, en particulier celles chargées. En fin de compte, il est devenu évident qu'une solution standard était nécessaire ici, qui serait acceptable pour dba, assurerait une livraison fiable et serait pratique pour le développement - c'est ainsi que nous avons écrit notre bibliothèque pour intégrer pgq et rabbitmq. Il y a maintenant une forte probabilité que nous utilisions pgq également en conjonction avec kafka.

1.0. Bugs


Les bogues sont également mondiaux. Par exemple, à un moment donné, nous avons découvert que notre framework python est enregistré en consul dans chaque processus-worker, et le fait même avant que l'application ne soit prête à accepter les requêtes. Après correction dans le cadre, les modifications atteindront progressivement tous les services au fur et à mesure de leur mise à jour.

J'ai parlé d'un autre bug général lié aux paramètres jvm au stade de démonstration jpoint 2019 .

Et que faire, par exemple, d'un bug qui se reproduit une fois par semaine sur l'une des instances, est traité avec un redémarrage, mais ni la charge ni les synthétiques ne le reproduisent?
, 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. Solutions générales


Parfois, il est possible de trouver des solutions standard avant que cela ne devienne un problème grave. À titre d'exemples, nous pouvons citer la tâche de traitement des journaux dont parlait notre Vlad Senin au même jpoint 2019 , ou la tâche de gestion des délais d'attente dans notre client http.

Cela signifie qu'il est utile de déterminer un délai d'expiration raisonnable non pas côté client, mais côté serveur. Pour le serveur, nous avons des données sur la rapidité avec laquelle il répond à ses points de terminaison. Maintenant, notre client prend en charge un délai d'attente pour le service. Mais il est évident que tous les points de terminaison de service ne répondent pas de la même manière - certains plus longtemps, certains plus rapidement. Je voudrais pouvoir utiliser différents délais d'expiration. Sinon, une situation similaire à celle-ci se présente:



Jusqu'à présent, de telles situations n'apparaissent que lors de tests de résistance, mais je veux les résoudre avant que cela ne devienne un problème.

1.2. Questions ouvertes


Mais tous les problèmes ne sont pas expliqués par des endroits importants et des processus manuels compliqués. De plus, je donnerai quelques exemples de questions qui entrent également dans le champ de nos priorités, mais qui sont en même temps beaucoup moins déterministes. Par conséquent, je ne décrirai que les données initiales et nous pourrons discuter des solutions si vous le souhaitez dans les commentaires.

Donc, le premier exemple: maintenant il devient clair qu'il y a un problème d'intégration de nos services entre eux. L'intégration, par exemple, d'un descripteur de site dans une API peut prendre plus de temps que son développement initial.

Un autre exemple, probablement connu de beaucoup, d'un problème similaire est le sciage d'un monolithe. Tout le monde comprend qu'un monolithe, recouvert d'un grand nombre d'héritage, complique le développement et l'exploitation. Mais qui peut dire combien? Vaut-il la peine de sacrifier d'autres tâches de la dette technique au profit du sciage, dont chaque pièce a individuellement une valeur disparate?

L'ampleur de ces problèmes et de problèmes similaires est telle que pour les résoudre, il faut parfois aller bien au-delà du cadre technique et plonger dans des domaines complètement nouveaux du processus de travail. C'est effrayant d'une part, mais d'autre part donne une liberté incroyable dans le choix des décisions.

2. Comment nous travaillons


L'histoire sur les directions de notre travail sera incomplète sans une description de COMMENT nous travaillons avec tout cela.

Pour commencer, ce qui m'a incité à travailler en «Architecture» et ce qui nous motive tous: nous travaillons vraiment pour la qualité.

Et avant que les pierres ne volent vers moi, je vais essayer d'expliquer ce que je veux dire. Je crois qu'aucun développeur ne marque délibérément la qualité. Le point est dans la dette technique: si nous parlons d'une section de la logique commerciale qui n'est pas prévue pour être réutilisée, alors le montant de la dette d'une solution pas si idéale augmentera probablement lentement avec le temps, voire pas du tout.

Cela vous permet de refroidir un peu votre perfectionnisme - commencez une tâche pour la dette et passez à la prochaine itération. Mais si nous parlons d'un cadre ou d'un outil de préparation de configuration globale qui est utilisé dans des centaines d'applications et consolide certains modèles de conception ou de dénomination, le taux de croissance de la dette résultant de sa décision infructueuse peut bloquer tous les gains. Il est clair qu'il y a des situations où même la meilleure solution révèle des faiblesses telles qu'elles sont utilisées, mais cela n'arrive pas souvent ...

Vers la fin, je voudrais parler des obstacles qui se dressent encore sur notre chemin. Sans cela, une histoire sur notre travail serait malhonnête. Donc.

2.0. Tâches d'évaluation de la difficulté


Comme je l'ai dit plus haut, nous ne pouvons pas évaluer l'effet bénéfique pour toutes les tâches. Combien le temps de libération des tâches diminuera-t-il lorsqu'une solution «en boîte» sortira pour une fonction? Laquelle des deux sections de code problématiques doit être refactorisée en premier? Pour développer un système adéquat pour évaluer les tâches, nous nous sommes rencontrés plusieurs fois par semaine pendant plusieurs mois, mais c'est un sujet pour un poste séparé.

2.1. Inconscient collectif


Coordonner quelque chose pour 150 personnes n'est pas une tâche facile. Notre structure organisationnelle très décentralisée se manifeste le plus souvent par ses meilleurs côtés, mais pour «Architecture» c'est parfois un sérieux obstacle. Il existe très peu d'accords auxquels il est possible de parvenir à un accord, encore moins ceux dont la conformité peut ensuite être contrôlée.

Et tous les changements doivent être effectués en douceur. Le service n'est peut-être pas mis à jour depuis des mois, mais il y a quand même un monolithe ... Bon, assez triste.

Nous avons donc parlé


J'espère qu'après mon histoire, j'ai un peu clarifié ce que fait «Architecture» dans hh.ru. Et si j'ai réussi à susciter votre intérêt pour notre travail, c'est généralement fantastique. De plus, tout à l'heure, un poste vacant est ouvert dans notre équipe . Nous serons très heureux pour de nouvelles idées qui nous aideront à réaliser nos regards cachés, mais de telles victoires importantes.

ps il s'avère que le KDPV original est une illustration de cet homme . J'espère qu'il n'est pas contre l'utilisation de ses images comme KDPV

All Articles