Клиент-серверное взаимодействие

Что такое клиент-серверная архитектура?

Клиент-серверная архитектура - это модель взаимодействия, где клиент запрашивает услуги или ресурсы, а сервер предоставляет их. Это основа работы веб-приложений, мобильных приложений и большинства современных IT-систем.

Зачем тестировщику понимать это:

  • Определять, где возникают проблемы (клиент или сервер)
  • Планировать тестирование API и интеграций
  • Понимать архитектуру приложения для лучшего покрытия тестами
  • Анализировать производительность и безопасность
  • Эффективно взаимодействовать с командой разработки

Основные компоненты

Клиент (Client)

Что это: Программа или устройство, которое отправляет запросы к серверу

Типы клиентов:

Веб-браузеры:
- Chrome, Firefox, Safari, Edge
- Отправляют HTTP запросы
- Отображают HTML, выполняют JavaScript

Мобильные приложения:
- iOS приложения (Swift/Objective-C)
- Android приложения (Java/Kotlin)
- React Native, Flutter приложения

Desktop приложения:
- Electron приложения (VS Code, Discord)
- Нативные приложения (Windows, macOS, Linux)

API клиенты:
- Postman, Insomnia
- curl, wget
- Другие сервисы и микросервисы

Сервер (Server)

Что это: Программа, которая принимает запросы от клиентов и отправляет ответы

Типы серверов:

Веб-серверы:
- Apache HTTP Server
- Nginx
- IIS (Internet Information Services)

Серверы приложений:
- Node.js + Express
- Java + Spring Boot
- Python + Django/Flask
- PHP + Laravel
- .NET + ASP.NET Core

Серверы баз данных:
- MySQL, PostgreSQL
- MongoDB, Redis
- Oracle, SQL Server

Специализированные серверы:
- Email серверы (SMTP, IMAP)
- DNS серверы
- File серверы (FTP, SSH)

Модели взаимодействия

Синхронное взаимодействие

Как работает:

  1. Клиент отправляет запрос
  2. Клиент ждет ответа
  3. Сервер обрабатывает запрос
  4. Сервер отправляет ответ
  5. Клиент получает ответ и продолжает работу

Пример HTTP запроса:

Клиент → Сервер: GET /api/users/123
Клиент ожидает...
Сервер → Клиент: {"id": 123, "name": "John", "email": "john@example.com"}

Характеристики:

  • Простота реализации
  • Предсказуемый порядок операций
  • Блокирование клиента на время запроса
  • Подходит для критических операций

Асинхронное взаимодействие

Как работает:

  1. Клиент отправляет запрос
  2. Клиент продолжает работу, не ожидая ответа
  3. Сервер обрабатывает запрос в фоне
  4. Сервер отправляет ответ когда готов
  5. Клиент получает ответ через callback/promise/event

Пример JavaScript:

// Асинхронный запрос
fetch('/api/users/123')
  .then(response => response.json())
  .then(user => {
    console.log('Получен пользователь:', user);
    updateUI(user);
  });

// Клиент продолжает выполнение других задач
console.log('Этот код выполнится сразу');

Характеристики:

  • Не блокирует интерфейс пользователя
  • Лучшая производительность
  • Сложность в обработке ошибок
  • Подходит для операций, не требующих немедленного результата

Протоколы взаимодействия

HTTP/HTTPS

HTTP (HyperText Transfer Protocol):

Характеристики:
✓ Простота использования
✓ Stateless (без состояния)
✓ Поддержка различных методов (GET, POST, PUT, DELETE)
✗ Передача данных в открытом виде
✗ Нет встроенной аутентификации

HTTPS (HTTP Secure):

HTTP + SSL/TLS шифрование
✓ Безопасная передача данных
✓ Аутентификация сервера
✓ Защита от прослушивания
✗ Дополнительная нагрузка на CPU
✗ Необходимость в SSL сертификатах

WebSocket

Характеристики:

  • Двунаправленная связь в реальном времени
  • Постоянное соединение
  • Низкая задержка

Применение:

Чаты и мессенджеры:
- Мгновенная доставка сообщений
- Уведомления о статусе "онлайн"

Онлайн игры:
- Синхронизация действий игроков
- Real-time мультиплеер

Финансовые приложения:
- Live котировки акций
- Уведомления о транзакциях

Collaborative editing:
- Google Docs, Notion
- Одновременное редактирование

gRPC

Характеристики:

  • Высокая производительность
  • Строгое типизирование (Protocol Buffers)
  • Поддержка streaming
  • Кроссплатформенность

Пример использования:

Микросервисная архитектура:
UserService ←→ OrderService ←→ PaymentService

API между внутренними сервисами:
- Быстрая передача данных
- Автогенерация клиентов
- Валидация типов на уровне протокола

Архитектурные паттерны

Monolith (Монолит)

Структура:

┌─────────────────────┐
│   Единое приложение │
│  ┌─────────────────┐│
│  │   Frontend      ││
│  ├─────────────────┤│
│  │   Backend       ││
│  ├─────────────────┤│
│  │   Database      ││
│  └─────────────────┘│
└─────────────────────┘

Преимущества:

  • Простота разработки и развертывания
  • Легкость тестирования
  • Отсутствие сетевых задержек между компонентами
  • Простая отладка

Недостатки:

  • Сложность масштабирования
  • Единая точка отказа
  • Технологическая привязка
  • Сложность изменений в большой кодовой базе

Microservices (Микросервисы)

Структура:

┌──────────┐    ┌──────────┐    ┌──────────┐
│ User     │    │ Order    │    │ Payment  │
│ Service  │    │ Service  │    │ Service  │
└────┬─────┘    └────┬─────┘    └────┬─────┘
     │               │               │
     └───────────────┼───────────────┘
                     │
             ┌───────┴──────┐
             │ API Gateway  │
             └───────┬──────┘
                     │
               ┌─────┴─────┐
               │  Frontend │
               └───────────┘

Преимущества:

  • Независимое развертывание сервисов
  • Масштабирование отдельных компонентов
  • Технологическое разнообразие
  • Устойчивость к отказам

Недостатки:

  • Сложность в тестировании интеграций
  • Сетевые задержки между сервисами
  • Сложность мониторинга и отладки
  • Необходимость в DevOps практиках

Serverless (Бессерверная архитектура)

Принцип: Код выполняется в облачных функциях по требованию

Примеры платформ:

AWS Lambda    - Amazon Web Services
Azure Functions    - Microsoft Azure
Google Cloud Functions    - Google Cloud Platform
Vercel Functions    - Frontend платформа

Применение в тестировании:

Автоматические тесты:
- Запуск тестов по триггерам
- Обработка результатов тестирования
- Уведомления о результатах

Тестовые данные:
- Генерация тестовых данных
- Очистка после тестов
- Mock сервисы

Обмен данными

Форматы данных

JSON (JavaScript Object Notation):

{
  "user": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "roles": ["user", "admin"],
    "active": true,
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

XML (eXtensible Markup Language):

<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>123</id>
  <name>John Doe</name>
  <email>john@example.com</email>
  <roles>
    <role>user</role>
    <role>admin</role>
  </roles>
  <active>true</active>
  <createdAt>2024-01-15T10:30:00Z</createdAt>
</user>

Protocol Buffers:

syntax = "proto3";

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  repeated string roles = 4;
  bool active = 5;
  string created_at = 6;
}

Методы HTTP запросов

GET - получение данных:

GET /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer token123

Ответ:
HTTP/1.1 200 OK
Content-Type: application/json

{"id": 123, "name": "John Doe"}

POST - создание данных:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer token123

{
  "name": "Jane Smith",
  "email": "jane@example.com"
}

Ответ:
HTTP/1.1 201 Created
Location: /api/users/124
Content-Type: application/json

{"id": 124, "name": "Jane Smith", "email": "jane@example.com"}

PUT - полное обновление:

PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "id": 123,
  "name": "John Updated",
  "email": "john.updated@example.com"
}

PATCH - частичное обновление:

PATCH /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "John Partially Updated"
}

DELETE - удаление:

DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer token123

Ответ:
HTTP/1.1 204 No Content

Состояние и сессии

Stateless vs Stateful

Stateless (без состояния):

Характеристики:
- Сервер не сохраняет информацию о клиенте между запросами
- Каждый запрос содержит всю необходимую информацию
- Легко масштабировать
- Простота в обслуживании

Пример:
GET /api/users/123
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI...

Каждый запрос содержит токен авторизации

Stateful (с состоянием):

Характеристики:
- Сервер сохраняет информацию о сессии клиента
- Клиент идентифицируется по session ID
- Сложнее масштабировать
- Требует управления состоянием

Пример:
1. POST /login → Set-Cookie: sessionId=abc123
2. GET /profile → Cookie: sessionId=abc123
3. Сервер знает, что abc123 принадлежит пользователю John

Управление сессиями

Cookies:

# Сервер устанавливает cookie
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

# Клиент отправляет cookie в последующих запросах
Cookie: sessionId=abc123

JWT Tokens:

// Токен содержит данные пользователя
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

// Декодированные данные
{
  "userId": 123,
  "email": "john@example.com",
  "roles": ["user"],
  "exp": 1643723400
}

Session Storage:

Серверная сессия:
SessionID: abc123
UserID: 123
LoginTime: 2024-01-15 10:30:00
LastActivity: 2024-01-15 11:45:00

Балансировка нагрузки

Load Balancer

Назначение: Распределение входящих запросов между несколькими серверами

Типы балансировки:

Round Robin:
Request 1 → Server A
Request 2 → Server B  
Request 3 → Server C
Request 4 → Server A (цикл повторяется)

Least Connections:
Request → Сервер с наименьшим количеством активных соединений

Weighted:
Server A (вес 3) получает 3 запроса
Server B (вес 1) получает 1 запрос

IP Hash:
Клиент с IP 192.168.1.100 всегда попадает на Server A

Практические аспекты для тестирования

Проблемы при балансировке:

Sticky Sessions:
- Пользователь должен попадать на тот же сервер
- Проблемы при падении сервера
- Сложность в тестировании failover

Session Replication:
- Данные сессии копируются между серверами
- Дополнительная нагрузка на сеть
- Возможны задержки синхронизации

Stateless подход:
- Лучше для масштабирования
- Не зависит от конкретного сервера
- Проще тестировать

Кэширование

Уровни кэширования

Browser Cache:

# Сервер указывает время кэширования
Cache-Control: max-age=3600
ETag: "abc123"

# Браузер сохраняет ресурс на 1 час
# При повторном запросе проверяет ETag

CDN (Content Delivery Network):

Пользователь в Москве → CDN сервер в Москве → Origin сервер в США

Преимущества:
- Быстрая загрузка статических ресурсов
- Снижение нагрузки на основной сервер
- Географическое распределение

Application Cache:

Redis/Memcached:
- Кэширование результатов запросов к БД
- Сессии пользователей
- Временные данные

Database Query Cache:
- Кэширование результатов SQL запросов
- Автоматическая инвалидация при изменении данных

Безопасность

Аутентификация и авторизация

Authentication (кто вы):

Basic Auth:
Authorization: Basic dXNlcjpwYXNzd29yZA==

Bearer Token:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Key:
X-API-Key: your-secret-api-key

Authorization (что можете делать):

Role-based:
User role: "admin" → может удалять пользователей
User role: "user" → может только просматривать профиль

Permission-based:
User permissions: ["read:users", "write:posts"]
Endpoint требует: "write:posts"

CORS (Cross-Origin Resource Sharing)

Проблема:

Frontend: https://myapp.com
API: https://api.myapp.com

Браузер блокирует запросы между разными доменами

Решение:

# Сервер API отправляет заголовки CORS
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

Практические сценарии тестирования

1. Тестирование производительности

Метрики для измерения:

Latency (задержка):
- Time to First Byte (TTFB)
- Response time
- Round-trip time

Throughput (пропускная способность):
- Requests per second (RPS)
- Bytes per second
- Concurrent users

Availability (доступность):
- Uptime percentage
- Error rate
- Recovery time

Инструменты:

# Apache Bench
ab -n 1000 -c 10 http://example.com/api/users

# curl с измерением времени
curl -w "@curl-format.txt" -o /dev/null -s http://example.com/

# wrk (современная альтернатива ab)
wrk -t12 -c400 -d30s http://example.com/

2. Тестирование отказоустойчивости

Сценарии тестирования:

Network failures:
- Потеря пакетов
- Высокая задержка (latency)
- Отключение сети

Server failures:
- Падение одного из серверов
- Перегрузка сервера
- Исчерпание ресурсов (CPU, память)

Database failures:
- Недоступность БД
- Медленные запросы
- Deadlock'и

3. Тестирование безопасности

Типы атак для тестирования:

SQL Injection:
POST /api/login
{"username": "admin'; DROP TABLE users; --", "password": "pass"}

XSS (Cross-Site Scripting):
POST /api/comments
{"text": "<script>alert('XSS')</script>"}

CSRF (Cross-Site Request Forgery):
<img src="http://api.example.com/transfer?amount=1000&to=attacker">

Rate Limiting:
Множественные запросы для проверки ограничений

4. Тестирование API интеграций

Чек-лист для API тестирования:

□ Все HTTP методы работают корректно
□ Статус-коды соответствуют ожиданиям
□ Валидация входных данных
□ Обработка ошибок
□ Форматы ответов (JSON schema validation)
□ Аутентификация и авторизация
□ Rate limiting
□ CORS настройки
□ Версионирование API
□ Документация актуальна

Мониторинг и наблюдаемость

Ключевые метрики

Application Performance Monitoring (APM):

Response time:
- Среднее время ответа
- 95th/99th percentile
- Тренды по времени

Error rate:
- Количество ошибок 4xx/5xx
- Типы ошибок
- Affected users

Throughput:
- Requests per minute
- Peak traffic handling
- Capacity planning

Логирование

Structured logging:

{
  "timestamp": "2024-01-15T10:30:00Z",
  "level": "INFO",
  "service": "user-service",
  "traceId": "abc123",
  "userId": 123,
  "action": "login",
  "duration": 250,
  "status": "success"
}

Distributed tracing:

Request ID: abc123

Frontend → API Gateway → User Service → Database
   1ms   →     50ms    →     200ms   →   100ms

Общее время: 351ms
Bottleneck: User Service (200ms)

Заключение

Понимание клиент-серверного взаимодействия критически важно для тестировщика:

  • Помогает определить scope тестирования - что тестировать на клиенте, что на сервере
  • Улучшает диагностику проблем - понимание где искать root cause
  • Позволяет планировать интеграционное тестирование - какие компоненты как взаимодействуют
  • Помогает в тестировании производительности - понимание bottlenecks
  • Улучшает коммуникацию с командой - общий язык с разработчиками

Современные приложения используют сложные архитектуры, и понимание основ клиент-серверного взаимодействия - это фундамент для эффективного тестирования.