Quarkus: модСрнизация ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ helloworld ΠΈΠ· JBoss EAP Quickstart (ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅)

ВсСм ΠΏΡ€ΠΈΠ²Π΅Ρ‚ – с Π²Π°ΠΌΠΈ пятый пост ΠΈΠ· нашСй сСрии ΠΏΡ€ΠΎ Quarkus! (ΠšΡΡ‚Π°Ρ‚ΠΈ, смотритС наш Π²Π΅Π±ΠΈΠ½Π°Ρ€ Β«Π­Ρ‚ΠΎ Quarkus – Kubernetes native Java Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΒ». ПокаТСм, ΠΊΠ°ΠΊ Π½Π°Ρ‡Π°Ρ‚ΡŒ «с нуля» ΠΈΠ»ΠΈ пСрСнСсти Π³ΠΎΡ‚ΠΎΠ²Ρ‹Π΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ)

Quarkus: модСрнизация ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ helloworld ΠΈΠ· JBoss EAP Quickstart (ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅)

Π’ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постС ΠΌΡ‹ рассмотрСли ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΡŽ Java-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Ρ… Quarkus Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ (CDI ΠΈ Servlet 3) Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ helloworld ΠΈΠ· рСпозитория Red Hat JBoss Enterprise Application Platform (JBoss EAP) Quickstart. БСгодня ΠΌΡ‹ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΠΌ Ρ‚Π΅ΠΌΡƒ ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈ обсудим вопрос потрСблСния памяти.

Π˜Π·ΠΌΠ΅Ρ€Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ – это Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Π°Ρ основа практичСски любой ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ, Π° ΠΎΡ‚Ρ‡Π΅Ρ‚Ρ‹ ΠΏΠΎ использованию памяти ΡΠ²Π»ΡΡŽΡ‚ΡΡ Π²Π°ΠΆΠ½ΠΎΠΉ Ρ‡Π°ΡΡ‚ΡŒΡŽ процСсса Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. БСгодня ΠΌΡ‹ рассмотрим ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ инструмСнты, с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΠΎΠΆΠ½ΠΎ количСствСнно ΠΎΡ†Π΅Π½ΠΈΡ‚ΡŒ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ Π² Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ Java-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ.

ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚ΠΈ ΠΎΠ± ΠΈΠ·ΠΌΠ΅Ρ€Π΅Π½ΠΈΠΈ использования памяти ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ·Π½Π°Ρ‚ΡŒ ΠΈΠ· руководства Quarkus ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ Measuring Performanceβ€”How do we measure memory usage?

А Π½ΠΈΠΆΠ΅ ΠΌΡ‹ просто ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ, ΠΊΠ°ΠΊ ΡΡ€Π°Π²Π½ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ использованию памяти трСмя Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ Ρ‚ΠΈΠΏΠ°ΠΌΠΈ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ (JBoss EAP, JAR-ΠΏΠ°ΠΊΠ΅Ρ‚ ΠΈ исполняСмый Ρ„Π°ΠΉΠ»), собирая эти Π΄Π°Π½Π½Ρ‹Π΅ Π² Linux с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚ pmap ΠΈ ps.

JBoss EAP

ЗапускаСм экзСмпляр прилоТСния JBoss EAP (см. Ρ€Π°Π·Π΄Π΅Π» Β«Π Π°Π·Π²ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Π΅ΠΌ helloworldΒ» Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постС) ΠΈ Π·Π°Ρ‚Π΅ΠΌ смотрим Π΅Π³ΠΎ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ процСсса PID (Π² нашСм ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ это 7268) с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹:

$ pgrep -lf jboss
7268 java

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅. ΠžΠΏΡ†ΠΈΡ –a позволяСт ΠΈΠ·Π²Π»Π΅Ρ‡ΡŒ ΠΏΠΎΠ»Π½ΡƒΡŽ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΡƒΡŽ строку (Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ: $ pgrep -af jboss).

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ PID 7268 Π² ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ… ps ΠΈ pmap.

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ:

$ ps -o pid,rss,command -p 7268
PID RSS COMMAND 
7268 665348 java -D[Standalone] -server -verbose:gc -Xloggc:/home/mrizzi/Tools/jboss-eap-7.2.0/jboss-eap-7.2/standalone/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=3M -XX:-TraceClassUnloading -Xms1303m -Xmx1303m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferI

И Π²ΠΎΡ‚ Ρ‚Π°ΠΊ:

$ pmap -x 7268
7268:   java -D[Standalone] -server -verbose:gc -Xloggc:/home/mrizzi/Tools/jboss-eap-7.2.0/jboss-eap-7.2/standalone/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=3M -XX:-TraceClassUnloading -Xms1303m -Xmx1303m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/mrizzi/Tools/jboss-eap-7.2.0/jboss-eap-7.2/standa
Address           Kbytes     RSS   Dirty Mode  Mapping
00000000ae800000 1348608  435704  435704 rw---   [ anon ]
0000000100d00000 1035264       0       0 -----   [ anon ]
000055e4d2c2f000       4       4       0 r---- java
000055e4d2c30000       4       4       0 r-x-- java
000055e4d2c31000       4       0       0 r---- java
000055e4d2c32000       4       4       4 r---- java
000055e4d2c33000       4       4       4 rw--- java
[...]
ffffffffff600000       4       0       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB         3263224  672772  643024

Π‘ΠΌΠΎΡ‚Ρ€ΠΈΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ RSS ΠΈ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ JBoss EAP потрСбляСт ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 650 ΠœΠ‘ памяти.

JAR-ΠΏΠ°ΠΊΠ΅Ρ‚

ЗапускаСм JAR-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ (см. Ρ€Π°Π·Π΄Π΅Π» «ЗапускаСм helloworld, ΡƒΠΏΠ°ΠΊΠΎΠ²Π°Π½Π½Ρ‹ΠΉ Π² JARΒ» Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постС):

$ java -jar ./target/helloworld-<version>-runner.jar

ΠžΠΏΡΡ‚ΡŒ смотрим PID с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ pgrep (Π½Π° этот Ρ€Π°Π· ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°Π½Π½ΡƒΡŽ Π²Ρ‹ΡˆΠ΅ ΠΎΠΏΡ†ΠΈΡŽ -a):

$ pgrep -af helloworld
6408 java -jar ./target/helloworld-<version>-runner.jar

ЗапускаСм ps ΠΈ pmap, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ·ΠΌΠ΅Ρ€ΠΈΡ‚ΡŒ использованиС памяти, Π½ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ для процСсса 6408.

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ:

$ ps -o pid,rss,command -p 6408
  PID   RSS COMMAND
 6408 125732 java -jar ./target/helloworld-quarkus-runner.jar

И Π²ΠΎΡ‚ Ρ‚Π°ΠΊ:

$ pmap -x 6408
6408:   java -jar ./target/helloworld-quarkus-runner.jar
Address           Kbytes     RSS   Dirty Mode  Mapping
00000005d3200000  337408       0       0 rw---   [ anon ]
00000005e7b80000 5046272       0       0 -----   [ anon ]
000000071bb80000  168448   57576   57576 rw---   [ anon ]
0000000726000000 2523136       0       0 -----   [ anon ]
00000007c0000000    2176    2088    2088 rw---   [ anon ]
00000007c0220000 1046400       0       0 -----   [ anon ]
00005645b85d6000       4       4       0 r---- java
00005645b85d7000       4       4       0 r-x-- java
00005645b85d8000       4       0       0 r---- java
00005645b85d9000       4       4       4 r---- java
00005645b85da000       4       4       4 rw--- java
[...]
ffffffffff600000       4       0       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB         12421844  133784  115692

ΠžΠΏΡΡ‚ΡŒ смотрим RSS ΠΈ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ JAR-ΠΏΠ°ΠΊΠ΅Ρ‚ потрСбляСт ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 130 ΠœΠ‘.

Π˜ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΡ‹ΠΉ Ρ„Π°ΠΉΠ»

ЗапускаСм Π½Π°Ρ‚ΠΈΠ²Π½ΠΈΠΊ (см. Ρ€Π°Π·Π΄Π΅Π» «ЗапускаСм Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ исполняСмый Ρ„Π°ΠΉΠ» helloworldΒ» Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постС):

$ ./target/helloworld-<version>-runner

ΠžΠΏΡΡ‚ΡŒ смотрим Π΅Π³ΠΎ PID:

$ pgrep -af helloworld
6948 ./target/helloworld-<version>-runner

И Π·Π°Ρ‚Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ процСсса (6948) Π² ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ… ps ΠΈ pmap.

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ:

$ ps -o pid,rss,command -p 6948
  PID   RSS COMMAND
 6948 19084 ./target/helloworld-quarkus-runner
И Π²ΠΎΡ‚ Ρ‚Π°ΠΊ:
$ pmap -x 6948
6948:   ./target/helloworld-quarkus-runner
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000      12      12       0 r---- helloworld-quarkus-runner
0000000000403000   10736    8368       0 r-x-- helloworld-quarkus-runner
0000000000e7f000    7812    6144       0 r---- helloworld-quarkus-runner
0000000001620000    2024    1448     308 rw--- helloworld-quarkus-runner
000000000181a000       4       4       4 r---- helloworld-quarkus-runner
000000000181b000      16      16      12 rw--- helloworld-quarkus-runner
0000000001e10000    1740     156     156 rw---   [ anon ]
[...]
ffffffffff600000       4       0       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB         1456800   20592    2684

Π‘ΠΌΠΎΡ‚Ρ€ΠΈΠΌ RSS ΠΈ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ исполняСмый Ρ„Π°ΠΉΠ» Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ ΠΎΠΊΠΎΠ»ΠΎ 20 ΠœΠ‘ памяти.

Π‘Ρ€Π°Π²Π½ΠΈΠ²Π°Π΅ΠΌ расход памяти

Π˜Ρ‚Π°ΠΊ, ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Ρ†ΠΈΡ„Ρ€Ρ‹ ΠΏΠΎ использованию памяти:

  • JBoss EAP – 650 ΠœΠ‘.
  • JAR-ΠΏΠ°ΠΊΠ΅Ρ‚ – 130 ΠœΠ‘.
  • Π˜ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΡ‹ΠΉ Ρ„Π°ΠΉΠ» – 20 ΠœΠ‘.

ΠžΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, Ρ‡Ρ‚ΠΎ исполняСмый Ρ„Π°ΠΉΠ» Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ Π³ΠΎΡ€Π°Π·Π΄ΠΎ мСньшС памяти.

Подводим ΠΈΡ‚ΠΎΠ³ΠΈ 4 ΠΈ 5 постов

Π’ этом ΠΈ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постах ΠΌΡ‹ рассмотрСли вопрос ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ Java-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ с использованиСм ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Ρ… Π² Quarkus Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ (CDI ΠΈ Servlet 3), Π° Ρ‚Π°ΠΊΠΆΠ΅ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ способы Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ, сборки ΠΈ запуска Ρ‚Π°ΠΊΠΈΡ… ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ. ΠœΡ‹ ΠΏΠΎΠΊΠ°Π·Π°Π»ΠΈ, ΠΊΠ°ΠΊ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ сбор Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΠΎ использованию памяти для ΠΎΡ†Π΅Π½ΠΊΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΠΉ, достигнутых Π² Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ Ρ‚Π°ΠΊΠΎΠΉ ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ. Π­Ρ‚ΠΈ ΡΡ‚Π°Ρ‚ΡŒΠΈ ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ ΠΏΠΎΠ½ΡΡ‚ΡŒ, ΠΊΠ°ΠΊ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Quarkus ΠΈ Π² Ρ‡Π΅ΠΌ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π΅Π³ΠΎ польза – Π²Π½Π΅ зависимости ΠΎΡ‚ Ρ‚ΠΎΠ³ΠΎ, ΠΈΠ΄Π΅Ρ‚ Π»ΠΈ Ρ€Π΅Ρ‡ΡŒ ΠΎ ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠ΅ΠΉ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΊΠ΅ helloworld ΠΈΠ· Π½Π°ΡˆΠΈΡ… ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² ΠΈΠ»ΠΈ ΠΎ Π³ΠΎΡ€Π°Π·Π΄ΠΎ Π±ΠΎΠ»Π΅Π΅ слоТных прилоТСниях ΠΈΠ· Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΉ ΠΆΠΈΠ·Π½ΠΈ.

ΠœΡ‹ вСрнСмся Ρ‡Π΅Ρ€Π΅Π· Π΄Π²Π΅ Π½Π΅Π΄Π΅Π»ΠΈ с Π·Π°ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ постом ΠΏΡ€ΠΎ Quarkus – Π΄ΠΎ встрСчи!

Π’ нашСм Π·Π°ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΌ постС ΠΌΡ‹ ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ, ΠΊΠ°ΠΊ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ AMQ Online ΠΈ Quarkus, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΡΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ систСму ΠΎΠ±ΠΌΠ΅Π½Π° сообщСниями Π½Π° Π±Π°Π·Π΅ OpenShift с использованиСм Π΄Π²ΡƒΡ… Π½ΠΎΠ²Ρ‹Ρ… Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ, связанных с ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΎΠΉ сообщСний. Π§ΠΈΡ‚Π°ΠΉΡ‚Π΅ ΠΏΠΎ ссылкС.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com