РПС (RPC, Remote Procedure Call) — это технология, которая позволяет программе вызывать функции (процедуры) на удалённых машинах, как если бы они были локальными. В контексте программирования на Go, РПС используется для организации взаимодействия между разными частями распределённых систем.
В современных приложениях часто необходимо взаимодействовать с различными сервисами, которые могут находиться на разных серверах. РПС позволяет реализовать это взаимодействие простым и прозрачным способом.
В архитектуре микросервисов разные части системы выполняются как независимые сервисы. РПС обеспечивает эффективное и удобное взаимодействие между этими сервисами.
РПС позволяет распределить нагрузку между несколькими серверами, что повышает производительность и масштабируемость системы.
net/rpc
, а также сторонние библиотеки, такие как gRPC, которые обеспечивают более широкие возможности и поддержку разных протоколов.// Сервисная часть
package main
import (
"net"
"net/rpc"
)
// Определяем структуру и методы, которые будут доступны через RPC
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
type Args struct {
A, B int
}
func main() {
arith := new(Arith)
rpc.Register(arith)
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("Listen error: ", err)
}
rpc.Accept(listener)
}
Multiply
для умножения двух чисел, и клиентскую часть, которая вызывает этот метод на удалённом сервере.// Клиентская часть
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
func main() {
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("Dialing error: ", err)
}
args := Args{2, 3}
var reply int
err = client.Call("Arith.Multiply", &args, &reply)
if err != nil {
log.Fatal("Arith error: ", err)
}
fmt.Printf("Result: %d\n", reply)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие преимущества использования пакетов в Go?
Anonymous Quiz
2%
Пакеты увеличивают размер исполняемого файла
2%
Пакеты автоматически увеличивают производительность программы
3%
Пакеты устраняют необходимость в компиляции
93%
Пакеты помогают организовать код и повторно использовать его
HTTPS (HyperText Transfer Protocol Secure) — это расширение протокола HTTP, которое обеспечивает безопасную передачу данных между веб-браузером пользователя и сервером. HTTPS использует протоколы шифрования, такие как TLS (Transport Layer Security) или его предшественник SSL (Secure Sockets Layer), чтобы защитить передаваемые данные от перехвата и манипуляций.
Защита данных от перехвата посторонними лицами. Данные, передаваемые между клиентом и сервером, шифруются с помощью симметричного шифрования, а ключи для этого шифрования обмениваются с использованием асимметричного шифрования.
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов. Сертификаты, выданные доверенными центрами сертификации (CA), подтверждают, что сервер принадлежит законному владельцу.
Обеспечение защиты данных от изменения во время передачи. Хеш-функции и контрольные суммы используются для проверки целостности передаваемых данных.
Клиент отправляет запрос на сервер, указывая, что хочет установить защищенное соединение (например, https://example.com). Сервер отвечает, отправляя свой цифровой сертификат, который содержит публичный ключ сервера и данные о сервере.
Клиент проверяет сертификат с помощью списка доверенных центров сертификации (CA), чтобы удостовериться в подлинности сервера.
Клиент и сервер используют асимметричное шифрование для обмена сессионными ключами. Эти ключи будут использоваться для симметричного шифрования данных во время сессии.
После установки защищенного соединения все данные между клиентом и сервером передаются в зашифрованном виде с использованием сессионных ключей.
Шифрование данных защищает их от перехвата и кражи, особенно на общественных сетях и Wi-Fi.
Наличие HTTPS улучшает доверие пользователей к веб-сайту, что может повысить количество посетителей и конверсии.
Поисковые системы, такие как Google, дают предпочтение сайтам, использующим HTTPS, в своих рейтингах.
HTTPS помогает защититься от атак типа "человек посередине" (MITM), при которых злоумышленники могут перехватывать и изменять данные.
Установка и поддержка защищенного соединения требует дополнительных вычислительных ресурсов, что может немного замедлить загрузку страниц.
Приобретение и обновление сертификатов могут требовать дополнительных затрат, хотя существуют и бесплатные решения, такие как Let’s Encrypt.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
SSL (Secure Sockets Layer) — это протокол, который обеспечивает безопасность передачи данных в интернете, используя шифрование. Он был разработан для защиты данных, передаваемых между клиентом (например, веб-браузером) и сервером (например, веб-сайтом), от перехвата и манипуляций.
Защита данных от перехвата и чтения посторонними лицами путем их шифрования.
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов, что позволяет клиенту убедиться, что он подключен к настоящему серверу.
Проверка того, что данные не были изменены во время передачи, с помощью контрольных сумм и хеш-функций.
Клиент инициирует соединение с сервером, запрашивая защищенное соединение.
Сервер отправляет свой цифровой сертификат, который содержит его публичный ключ и информацию о сервере.
Клиент проверяет сертификат, используя доверенные центры сертификации (CA), чтобы удостовериться в подлинности сервера.
Клиент и сервер используют асимметричное шифрование для обмена ключами сеанса, которые затем используются для симметричного шифрования данных в течение сессии.
Все данные, передаваемые между клиентом и сервером, шифруются с использованием симметричных ключей, обеспечивая безопасность передачи.
При посещении веб-сайта с использованием HTTPS (например, https://example.com), SSL обеспечивает шифрование и безопасность данных, передаваемых между вашим браузером и сервером.
Никогда не был выпущен публично из-за серьезных уязвимостей.
Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).
SSL был заменен протоколом TLS, который является его преемником и предлагает улучшенную безопасность. Текущие версии TLS (1.2 и 1.3) используются вместо SSL.
TLS обеспечивает более сильное шифрование, лучшее управление сессионными ключами и устранение уязвимостей, найденных в SSL.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
TLS (Transport Layer Security) — протокол безопасности, который защищает данные, передаваемые через интернет, используя шифрование. TLS является преемником SSL (Secure Sockets Layer) и обеспечивает конфиденциальность, целостность и аутентификацию данных.
Защита данных от перехвата и чтения посторонними лицами. Данные шифруются с использованием симметричного шифрования, а ключи обмениваются асимметрично.
Подтверждение подлинности сервера и клиента с помощью цифровых сертификатов, выданных доверенными центрами сертификации (CA).
Защита данных от изменения во время передачи с помощью контрольных сумм и хеш-функций.
Клиент отправляет запрос на сервер для установки защищенного соединения.
Сервер отправляет свой цифровой сертификат, который клиент проверяет с помощью CA.
Клиент и сервер обмениваются сессионными ключами, используя асимметричное шифрование, и далее используют их для симметричного шифрования данных.
Все данные передаются в зашифрованном виде.
Улучшение по сравнению с SSL 3.0, но устаревший.
Дополнительные защиты, но также устаревший.
Современные алгоритмы шифрования, улучшенная безопасность.
Современные алгоритмы шифрования и хеширования.
TLS 1.3 снижает задержки при установке соединения.
Поддерживается большинством современных браузеров и серверов.
Особенно для крупных систем.
Шифрование требует дополнительных вычислительных ресурсов.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TLS Example</title>
</head>
<body>
<h1>Welcome to Secure Site</h1>
<p>Your connection to this site is secure with HTTPS using TLS.</p>
</body>
</html>
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Да, между SSL (Secure Sockets Layer) и TLS (Transport Layer Security) есть отличия. TLS является улучшенной и более безопасной версией SSL.
SSL 1.0: Никогда не был выпущен публично из-за серьезных уязвимостей.
SSL 2.0: Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
SSL 3.0: Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).
TLS 1.0: Выпущен в 1999 году как обновление SSL 3.0. Включает исправления безопасности и улучшения.
TLS 1.1: Выпущен в 2006 году с дополнительными защитами от некоторых атак.
TLS 1.2: Выпущен в 2008 году, поддерживает современные алгоритмы шифрования и хеширования.
TLS 1.3: Выпущен в 2018 году, значительно улучшена безопасность и производительность, упрощен процесс установки соединения.
SSL: Поддерживает более старые и менее безопасные алгоритмы шифрования.
TLS: Поддерживает более современные и безопасные алгоритмы шифрования. TLS 1.3 исключает поддержку устаревших алгоритмов и предлагает только современные безопасные алгоритмы.
SSL: Более сложный процесс рукопожатия, включающий несколько шагов, что делает его уязвимым для некоторых атак.
TLS: Улучшенный процесс рукопожатия, включая использование HMAC (Hash-based Message Authentication Code) для обеспечения целостности сообщения. TLS 1.3 значительно упрощает и ускоряет процесс рукопожатия.
SSL: Использует комбинацию MD5 и SHA-1 для целостности данных, что не так безопасно по современным стандартам.
TLS: Использует HMAC с SHA-256 и другими современными алгоритмами для обеспечения целостности данных.
SSL: Меньше возможностей для управления сеансами.
TLS: Включает улучшенные механизмы для управления сеансами, такие как возобновление сеансов, что позволяет экономить время и ресурсы при повторных подключениях.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Для проверки типа переменной во время выполнения используется конструкция type assertion:
value, ok := x.(T)
, где T
— предполагаемый тип, а ok
указывает, совпал ли тип. Также можно использовать type switch: switch v := x.(type) { case int: // действия для int case string: // действия для string default: // другие типы }
.Пакет
reflect
предоставляет механизмы для получения информации о типах переменных во время выполнения.package main
import (
"fmt"
"reflect"
)
func main() {
var x interface{} = 42
// Получаем тип переменной x
t := reflect.TypeOf(x)
fmt.Println("Type:", t)
}
Этот метод позволяет проверить, соответствует ли переменная конкретному типу, и извлечь значение, если это так.
package main
import "fmt"
func main() {
var x interface{} = 42
// Проверка типа и извлечение значения
if val, ok := x.(int); ok {
fmt.Println("x is an int:", val)
} else {
fmt.Println("x is not an int")
}
}
Этот метод удобен, если нужно проверить переменную на соответствие нескольким типам.
package main
import "fmt"
func main() {
var x interface{} = "Hello, World!"
switch v := x.(type) {
case int:
fmt.Println("x is an int:", v)
case string:
fmt.Println("x is a string:", v)
case bool:
fmt.Println("x is a bool:", v)
default:
fmt.Println("x is of unknown type")
}
}
reflect.TypeOf(x)
: Возвращает объект типа reflect.Type
, который содержит информацию о типе переменной x
.x.(int)
: Проверяет, является ли x
переменной типа int
, и возвращает значение и булево значение, указывающее, удалось ли утверждение типа.switch v := x.(type)
: Позволяет проверить переменную x
на соответствие нескольким типам в одном блоке switch
.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это представляет собой сложную задачу из-за множества компонентов, взаимодействующих друг с другом в различных сетевых условиях.
Проверка корректности отдельных компонентов системы. Используйте библиотеки для написания тестов, такие как
testing
в Go. Интеграционное тестирование Моки (mocking) и заглушки (stubs) для изоляции компонентов.package main
import (
"testing"
)
func TestAdd(t *testing.T) {
result := Add(1, 2)
if result != 3 {
t.Errorf("Expected 3, got %d", result)
}
}
Проверка взаимодействия между компонентами системы.
Используйте контейнеризацию (например, Docker) для поднятия среды тестирования. Тестирование с использованием реальных баз данных, очередей сообщений и других внешних сервисов.
version: '3'
services:
app:
image: myapp:test
depends_on:
- db
db:
image: postgres:latest
environment:
POSTGRES_DB: testdb
POSTGRES_USER: user
POSTGRES_PASSWORD: password
Проверка всей системы в целом, включая все её компоненты и их взаимодействие. Сценарии end-to-end (E2E) тестирования. Тестирование пользовательского интерфейса и API. Selenium для тестирования веб-интерфейсов. Postman или REST-assured для тестирования API.
Проверка системы на устойчивость к высоким нагрузкам и определение её производительности. Нагрузочные тесты для проверки, как система справляется с увеличением числа пользователей или запросов. Тесты стресс-тестирования для выявления пределов производительности системы. Тесты стабильности для проверки системы под постоянной нагрузкой в течение длительного времени. Apache JMeter Gatling Locust
Проверка способности системы справляться с отказами отдельных компонентов или сетевых сбоев. Имитация отказов компонентов (например, остановка сервиса или отключение узла).
Инструменты для хаос-тестирования (chaos engineering). Chaos Monkey от Netflix Gremlin
Проверка системы на наличие уязвимостей и обеспечение её защиты от атак.Сканы уязвимостей. Тестирование на проникновение (penetration testing). OWASP ZAP Burp Suite
Интеграция тестирования в процесс CI/CD для автоматизации и ускорения релизов. Настройка CI/CD конвейера для автоматического выполнения тестов при каждом изменении кода. Использование инструментов, таких как Jenkins, GitLab CI/CD или CircleCI.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Lock-free (без блокировок) концепция используется в параллельном и многопоточном программировании для создания алгоритмов, которые позволяют потокам работать с общими данными без традиционных блокировок, таких как мьютексы. Это помогает избежать проблем, связанных с блокировками, таких как взаимные блокировки и задержки.
Не используются мьютексы или другие блокирующие примитивы для управления доступом к ресурсам.
Обеспечивается параллельный доступ к данным, что повышает производительность многопоточных приложений.
Используются атомарные операции для предотвращения состояний гонки.
Атомарные операции гарантируют, что операция выполняется полностью или не выполняется вовсе, без промежуточных состояний.
Сравнивает текущее значение переменной с ожидаемым и, если они совпадают, обновляет её новым значением.
Атомарно увеличивает значение переменной и возвращает старое значение.
Используются атомарные операции для безопасного добавления и удаления элементов.
package main
import (
"fmt"
"sync/atomic"
"unsafe"
)
type Node struct {
value int
next *Node
}
type Stack struct {
head *Node
}
func (s *Stack) Push(value int) {
newNode := &Node{value: value}
for {
oldHead := s.head
newNode.next = oldHead
if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&s.head)), unsafe.Pointer(oldHead), unsafe.Pointer(newNode)) {
break
}
}
}
func (s *Stack) Pop() (int, bool) {
for {
oldHead := s.head
if oldHead == nil {
return 0, false
}
newHead := oldHead.next
if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&s.head)), unsafe.Pointer(oldHead), unsafe.Pointer(newHead)) {
return oldHead.value, true
}
}
}
func main() {
stack := &Stack{}
stack.Push(1)
stack.Push(2)
value, ok := stack.Pop()
if ok {
fmt.Println("Popped:", value)
} else {
fmt.Println("Stack is empty")
}
}
Потоки не блокируют друг друга, что устраняет проблему deadlock.
Параллельный доступ к данным без блокировок повышает производительность.
Снижаются затраты на контекстные переключения и синхронизацию.
Lock-free алгоритмы сложны в реализации и отладке.
Не все языки и платформы полностью поддерживают атомарные операции.
Частые повторные попытки атомарных операций могут ухудшить масштабируемость.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это мера зависимости одного модуля или компонента системы от других модулей. Высокое сцепление означает, что модули сильно зависят друг от друга, в то время как низкое сцепление указывает на слабую взаимосвязь между модулями.
Модули сильно зависят друг от друга.
Изменения в одном модуле могут потребовать изменений в других.
Сложнее тестировать и поддерживать код.
type User struct {
Name string
Age int
}
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
type UserRepository struct{}
func (r *UserRepository) FindById(id int) *User {
// Логика доступа к базе данных
return &User{Name: "John", Age: 30}
}
Модули имеют минимальные зависимости друг от друга.
Легче изменять, тестировать и повторно использовать код.
Использование интерфейсов и зависимостей через инъекцию (Dependency Injection).
type User struct {
Name string
Age int
}
type UserRepository interface {
FindById(id int) *User
}
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
Это мера того, насколько элементы внутри модуля или компонента связаны и работают вместе для выполнения одной задачи. Высокая связанность означает, что элементы внутри модуля тесно связаны и фокусируются на одной задаче, в то время как низкая связанность указывает на наличие разнородных элементов, выполняющих несвязанные задачи.
Модуль выполняет несколько различных задач.
Трудно поддерживать и изменять код.
Класс, выполняющий и доступ к базе данных, и логику бизнес-правил, и обработку пользовательского интерфейса.
type UserService struct {
// Доступ к базе данных
db Database
// Логика бизнес-правил
businessLogic BusinessLogic
// Логика уведомлений
notificationService NotificationService
}
func (s *UserService) ProcessUser(id int) {
user := s.db.FindUserById(id)
s.businessLogic.ApplyRules(user)
s.notificationService.Notify(user)
}
Модуль сфокусирован на выполнении одной задачи.
Легче понимать, поддерживать и изменять код.
Класс, выполняющий только одну роль, такую как управление пользователями или обработка платежей.
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это инструмент для профилирования производительности программ на Go, который позволяет собирать и анализировать информацию о потреблении ресурсов (таких как память и процессорное время). Он предоставляет удобные инструменты для визуализации и анализа профилей.
Для включения профилирования в вашем приложении, вам нужно импортировать пакет
net/http/pprof
и запустить HTTP-сервер.package main
import (
_ "net/http/pprof"
"log"
"net/http"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Ваше основное приложение
select {}
}
После запуска приложения с включенным
pprof
, вы можете собирать профили памяти с помощью HTTP-запросов. go tool pprof http://localhost:6060/debug/pprof/heap
Для получения профиля CPU:
go tool pprof http://localhost:6060/debug/pprof/profile
После того как вы собрали профиль, вы можете анализировать его с помощью команды
go tool pprof
. Это откроет интерактивный интерфейс, где вы можете использовать различные команды для анализа профиля.go tool pprof http://localhost:6060/debug/pprof/heap
Показывает функции, которые потребляют больше всего памяти.
Показывает использование памяти в конкретной функции.
Генерирует и открывает графический отчет в веб-браузере.
package main
import (
_ "net/http/pprof"
"log"
"net/http"
"time"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Создаем массив для потребления памяти
data := make([]byte, 100*1024*1024) // 100MB
// Заполняем массив данными
for i := range data {
data[i] = byte(i % 256)
}
// Ожидаем завершения приложения
time.Sleep(time.Hour)
}
pprof предоставляет детализированную информацию о потреблении памяти и процессорного времени.
Возможность визуализации профилей в виде графов и диаграмм.
Легко интегрируется в Go-приложения.
Профилирование может добавить накладные расходы, что может повлиять на производительность приложения.
Интерпретация результатов профилирования может быть сложной задачей и требует определенных навыков и опыта.
Ставь 👍 и забирай 📚 Базу знани
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
pprof позволяет профилировать производительность приложения в реальном времени, собирая данные о загрузке процессора, памяти и других метриках. Включите pprof в ваше приложение и запустите его на продакшене:
package main
import (
_ "net/http/pprof"
"log"
"net/http"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
select {}
}
Затем соберите и проанализируйте профили:
go tool pprof http://localhost:6060/debug/pprof/profile
Помогает отслеживать производительность и выявлять аномалии. Логируйте время выполнения критических операций и ошибки. Используйте библиотеку
log
или сторонние решения, такие как logrus
.package main
import (
"log"
"time"
)
func main() {
start := time.Now()
// Выполнение задачи
log.Printf("Task completed in %s", time.Since(start))
}
Позволяет отслеживать путь запроса через различные сервисы и компоненты системы, выявляя медленные части. Используйте OpenTelemetry для интеграции трассировки.
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
func main() {
tracer := otel.Tracer("example-tracer")
ctx, span := tracer.Start(context.Background(), "example-span")
defer span.End()
// Ваш код здесь
}
Собирайте метрики производительности и настройте алерты для выявления аномалий. Используйте Prometheus для сбора метрик и Grafana для визуализации.
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
)
var (
requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "Duration of HTTP requests.",
})
)
func init() {
prometheus.MustRegister(requestDuration)
}
func handler(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(requestDuration)
defer timer.ObserveDuration()
// Ваш код здесь
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Помогает выявить ошибки и проблемы производительности. Используйте системы сбора логов, такие как ELK Stack (Elasticsearch, Logstash, Kibana).
Облачные сервисы мониторинга, такие как New Relic, Datadog или AWS CloudWatch, предоставляют мощные инструменты для мониторинга и анализа производительности.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM