Commit 46f27ebd authored by avelichko's avatar avelichko

Initial commit

parents
# Языки написания спецификаций. Задание 2
Автор: **Величко Арсений, ИВТ 4**
Тема моей ВКР - "Оптимизация выполнения serverless-функций на облачных инфраструктурах на основе импортозамещения". Разрабатываемый программный продукт представляет собой программный комплекс для автоматизированного запуска и замера производительности serverless-функций на облачной инфраструктуре импортозамещения, включающий набор тестовых сценариев, модуль сбора метрик и инструмент сравнения показателей до и после оптимизации. В качестве пользовательского интерфейса реализуется веб-портал с визуализацией результатов испытаний и формулировкой рекомендаций по эффективному развертыванию и конфигурации функций.
# Диаграммы
Для генерации диаграмм использовался ИИ ChatGPT o4-mini-high от OpenAI. Промпт:
> Ты - студент четвертого курса специальности "Информатика и вычислительная техника". Ты пишешь ВКР по теме "Оптимизация выполнения serverless-функций на облачных инфраструктурах на основе импортозамещения". Разрабатываемый программный продукт представляет собой программный комплекс для автоматизированного запуска и замера производительности serverless-функций на облачной инфраструктуре импортозамещения, включающий набор тестовых сценариев, модуль сбора метрик и инструмент сравнения показателей до и после оптимизации. В качестве пользовательского интерфейса реализуется веб-портал с визуализацией результатов испытаний и формулировкой рекомендаций по эффективному развертыванию и конфигурации функций.
>
> Используя PlantUML, создай набор диаграмм, раскрывающих функционал разрабатываемого продукта в рамках ВКР. Диаграммы должны раскрывать динамическую и статическую сущность разрабатываемого продукта.
>
> Необходимо реализовать следующие диаграммы:
> - вариантов использования (use case);
> - классов (classes);
> - последовательности (sequence);
> - диаграмма состояний (state);
> - диаграмма деятельности (activity).
>
> Для названия элементов используй русский язык
## Диаграмма вариантов использования (Use Case)
Показывает, кто (акторы) взаимодействует с системой и какие функции (варианты использования) она поддерживает. Помогает зафиксировать требования на высоком уровне: какие сценарии выполнения задач имеются у пользователей и как они входят/выходят за границы системы.
### Исходный код диаграммы
```plantuml
@startuml UseCase
title Диаграмма вариантов использования комплекса тестирования Serverless
left to right direction
actor ОблачныйИнженер as "Облачный инженер"
rectangle "Комплекс тестирования Serverless" {
(Настроить тестовые сценарии) as UC1
(Запустить тесты) as UC2
(Просмотреть результаты) as UC3
(Сгенерировать рекомендации) as UC4
}
ОблачныйИнженер --> UC1
ОблачныйИнженер --> UC2
ОблачныйИнженер --> UC3
ОблачныйИнженер --> UC4
@enduml
```
### Результат
![Use Case Diagram](diagrams/UseCase.png)
## Диаграмма классов (Class Diagram)
Отображает статическую структуру системы: её классы (или сущности), их атрибуты и методы, а также связи между ними (наследование, ассоциации, зависимости и т. д.). Помогает понять, из каких компонентов строится приложение и как они взаимодействуют на уровне типов.
### Исходный код диаграммы
```plantuml
@startuml ClassDiagram
title Диаграмма классов системы тестирования Serverless
class СценарийТеста {
+id: String
+имя: String
+конфигурация: Map<String, Object>
+выполнить(): Результат
}
class Оптимизатор {
+оптимизировать(сценарий: СценарийТеста): СценарийТеста
}
class СборщикМетрик {
+собратьМетрики(результат: Результат): Метрики
}
class ИнструментСравнения {
+сравнить(до: Метрики, после: Метрики): ОтчетСравнения
}
class ГенераторРекомендаций {
+сгенерироватьРекомендации(отчет: ОтчетСравнения): List<String>
}
class ВебПортал {
+показатьРезультаты(отчет: ОтчетСравнения)
+показатьРекомендации(рекомендации: List<String>)
}
class Результат {
+данные: Object
}
class Метрики {
+время: Integer
+ресурсы: Map<String, Integer>
}
class ОтчетСравнения {
+улучшения: Map<String, Double>
}
СценарийТеста --> Результат : "1"
Оптимизатор --> СценарийТеста : оптимизирует
СборщикМетрик --> Метрики : собирает
ИнструментСравнения --> ОтчетСравнения : создает отчет
ГенераторРекомендаций --> "List<String>" : рекомендации
ВебПортал --> ОтчетСравнения : отображает отчет
ВебПортал --> "List<String>" : отображает рекомендации
@enduml
```
### Результат
![Class Diagram](diagrams/ClassDiagram.png)
## Диаграмма последовательности (Sequence Diagram)
Показывает динамику взаимодействия между объектами или компонентами в ходе выполнения конкретного сценария. По горизонтали — участники, по вертикали — временная шкала: кто и в каком порядке вызывает какие методы, и какие ответы получает.
### Исходный код диаграммы
```plantuml
@startuml SequenceDiagram
title Диаграмма последовательности выполнения тестов Serverless
actor ОблачныйИнженер
participant ВебПортал
participant ДвижокТестирования as TestEngine
participant Оптимизатор
participant СборщикМетрик
participant ИнструментСравнения
participant ГенераторРекомендаций
ОблачныйИнженер -> ВебПортал: настроитьТест(конфигСценария)
ВебПортал -> ДвижокТестирования: запуститьТест(конфигСценария)
ДвижокТестирования -> Оптимизатор: оптимизировать(сценарий)
Оптимизатор --> ДвижокТестирования: оптимизированныйСценарий
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оптимизированныйСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОптимизированного
ДвижокТестирования --> ВебПортал: результатыОптимизированного
ВебПортал -> ДвижокТестирования: запуститьТест(оригСценарий)
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оригСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОригинала
ДвижокТестирования -> ИнструментСравнения: сравнить(метрикиОригинала, метрикиОптимизированного)
ИнструментСравнения --> ВебПортал: отчетСравнения
ВебПортал -> ГенераторРекомендаций: сгенерироватьРекомендации(отчетСравнения)
ГенераторРекомендаций --> ВебПортал: рекомендации
ВебПортал --> ОблачныйИнженер: отобразить(отчетСравнения, рекомендации)
@enduml
```
### Результат
![Sequence Diagram](diagrams/SequenceDiagram.png)
## Диаграмма состояний (State Diagram)
Описывает жизненный цикл единственного объекта или сущности: набор возможных состояний, в которых он может находиться, и переходы между ними (события и условия перехода). Полезна для моделирования поведения компонентов с явно выраженными стадиями.
### Исходный код диаграммы
```plantuml
@startuml StateDiagram
title Диаграмма состояний процесса тестирования Serverless
[*] --> Создано
Создано --> Оптимизировано : оптимизировать()
Оптимизировано --> Выполняется : выполнить()
Выполняется --> МетрикиСобраны : собратьМетрики()
МетрикиСобраны --> Сравнение : сравнить()
Сравнение --> Рекомендации : сгенерироватьРекомендации()
Рекомендации --> Просмотр : просмотреть()
Просмотр --> [*]
@enduml
```
### Результат
![State Diagram](diagrams/StateDiagram.png)
## Диаграмма деятельности (Activity Diagram)
Моделирует процесс или рабочий поток: последовательность действий (активностей), ветвления, параллельных потоков и точек синхронизации. Помогает визуализировать бизнес-процессы или алгоритмы внутри системы.
### Исходный код диаграммы
```plantuml
@startuml ActivityDiagram
title Диаграмма активности процесса тестирования Serverless
start
:Настроить тестовый сценарий;
:Оптимизировать сценарий;
:Запустить оптимизированный тест;
:Собрать метрики оптимизированного теста;
:Запустить оригинальный тест;
:Собрать метрики оригинального теста;
:Сравнить метрики;
:Сгенерировать рекомендации;
:Отобразить результаты;
stop
@enduml
```
### Результат
![Activity Diagram](diagrams/ActivityDiagram.png)
# Код на Python
Для предложенной диаграммы классов был сгенерирован Python модуль. Для генерации кода использовался IDE Cursor и ИИ Claude 3.7 Sonnet от Anthropic. Промпт:
> Given this class diagram, generate a Python module containing all the classes and relationships. Translate all class, method and field names into English. Do not implement the methods, but define their signatures. Use modern Python 3.12 syntax. Follow PEP8 style. Do not write comments. Clean code
### Результат
```python
from typing import Any
from dataclasses import dataclass
@dataclass
class Result:
data: Any
@dataclass
class Metrics:
time: int
resources: dict[str, int]
@dataclass
class ComparisonReport:
improvements: dict[str, float]
class TestScenario:
def __init__(self, id: str, name: str, configuration: dict[str, Any]) -> None:
self.id = id
self.name = name
self.configuration = configuration
def execute(self) -> Result:
pass
class Optimizer:
def optimize(self, scenario: TestScenario) -> TestScenario:
pass
class MetricsCollector:
def collect_metrics(self, result: Result) -> Metrics:
pass
class ComparisonTool:
def compare(self, before: Metrics, after: Metrics) -> ComparisonReport:
pass
class RecommendationGenerator:
def generate_recommendations(self, report: ComparisonReport) -> list[str]:
pass
class WebPortal:
def show_results(self, report: ComparisonReport) -> None:
pass
def show_recommendations(self, recommendations: list[str]) -> None:
pass
```
# Прототип интерфейса
Не так просто перенести резульат работы LLM в популярные платформы для прототипирований интерфейсов, такие как Figma. Однако, LLM отлично справляются с написанием frontend кода. Используя IDE Cursor и ИИ Claude 3.7 Sonnet от Anthropic, я сгенерировал прототип веб приложения. Для этого был использован следующий промпт:
> You are a senior frontend developer tasked with creating a wireframe for a new application your company is developing. Your Russian-speaking boss provided you with this product description:
>
> Разрабатываемый программный продукт представляет собой программный комплекс для автоматизированного запуска и замера производительности serverless-функций на облачной инфраструктуре импортозамещения, включающий набор тестовых сценариев, модуль сбора метрик и инструмент сравнения показателей до и после оптимизации. В качестве пользовательского интерфейса реализуется веб-портал с визуализацией результатов испытаний и формулировкой рекомендаций по эффективному развертыванию и конфигурации функций.
>
> Come up with a web interface for this application and prerpare a basic wireframe using a modern frontend development stack of your choice. Make sure to implement the home page, a page with in-depth testing and performance insights, and a page for configuring the testing. Use fake data for your graphs and charts and populate all the text fields with appropiate sample text. Use Russian as the main language for the interface and sample data. Place all your code inside the "wireframe" directory. Refer to the attached README.md file for some technical aspects of the supposed app. Then, prepare a Dockerfile for your application. Give this task all you have as your annual bonus is on the line.
Приложение находится в директории "wireframe". Приложение может быть запущено командой `docker-compose up --build`.
### Скриншоты
![Screenshot](assets/home-page.png)
![Screenshot](assets/test-page.png)
![Screenshot](assets/results-page.png)
![Screenshot](assets/config-page.png)
from typing import Any
from dataclasses import dataclass
@dataclass
class Result:
data: Any
@dataclass
class Metrics:
time: int
resources: dict[str, int]
@dataclass
class ComparisonReport:
improvements: dict[str, float]
class TestScenario:
def __init__(self, id: str, name: str, configuration: dict[str, Any]) -> None:
self.id = id
self.name = name
self.configuration = configuration
def execute(self) -> Result:
pass
class Optimizer:
def optimize(self, scenario: TestScenario) -> TestScenario:
pass
class MetricsCollector:
def collect_metrics(self, result: Result) -> Metrics:
pass
class ComparisonTool:
def compare(self, before: Metrics, after: Metrics) -> ComparisonReport:
pass
class RecommendationGenerator:
def generate_recommendations(self, report: ComparisonReport) -> list[str]:
pass
class WebPortal:
def show_results(self, report: ComparisonReport) -> None:
pass
def show_recommendations(self, recommendations: list[str]) -> None:
pass
\ No newline at end of file
@startuml ActivityDiagram
title Диаграмма активности процесса тестирования Serverless
start
:Настроить тестовый сценарий;
:Оптимизировать сценарий;
:Запустить оптимизированный тест;
:Собрать метрики оптимизированного теста;
:Запустить оригинальный тест;
:Собрать метрики оригинального теста;
:Сравнить метрики;
:Сгенерировать рекомендации;
:Отобразить результаты;
stop
@enduml
\ No newline at end of file
@startuml ClassDiagram
title Диаграмма классов системы тестирования Serverless
class СценарийТеста {
+id: String
+имя: String
+конфигурация: Map<String, Object>
+выполнить(): Результат
}
class Оптимизатор {
+оптимизировать(сценарий: СценарийТеста): СценарийТеста
}
class СборщикМетрик {
+собратьМетрики(результат: Результат): Метрики
}
class ИнструментСравнения {
+сравнить(до: Метрики, после: Метрики): ОтчетСравнения
}
class ГенераторРекомендаций {
+сгенерироватьРекомендации(отчет: ОтчетСравнения): List<String>
}
class ВебПортал {
+показатьРезультаты(отчет: ОтчетСравнения)
+показатьРекомендации(рекомендации: List<String>)
}
class Результат {
+данные: Object
}
class Метрики {
+время: Integer
+ресурсы: Map<String, Integer>
}
class ОтчетСравнения {
+улучшения: Map<String, Double>
}
СценарийТеста --> Результат : "1"
Оптимизатор --> СценарийТеста : оптимизирует
СборщикМетрик --> Метрики : собирает
ИнструментСравнения --> ОтчетСравнения : создает отчет
ГенераторРекомендаций --> "List<String>" : рекомендации
ВебПортал --> ОтчетСравнения : отображает отчет
ВебПортал --> "List<String>" : отображает рекомендации
@enduml
\ No newline at end of file
@startuml SequenceDiagram
title Диаграмма последовательности выполнения тестов Serverless
actor ОблачныйИнженер
participant ВебПортал
participant ДвижокТестирования as TestEngine
participant Оптимизатор
participant СборщикМетрик
participant ИнструментСравнения
participant ГенераторРекомендаций
ОблачныйИнженер -> ВебПортал: настроитьТест(конфигСценария)
ВебПортал -> ДвижокТестирования: запуститьТест(конфигСценария)
ДвижокТестирования -> Оптимизатор: оптимизировать(сценарий)
Оптимизатор --> ДвижокТестирования: оптимизированныйСценарий
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оптимизированныйСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОптимизированного
ДвижокТестирования --> ВебПортал: результатыОптимизированного
ВебПортал -> ДвижокТестирования: запуститьТест(оригСценарий)
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оригСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОригинала
ДвижокТестирования -> ИнструментСравнения: сравнить(метрикиОригинала, метрикиОптимизированного)
ИнструментСравнения --> ВебПортал: отчетСравнения
ВебПортал -> ГенераторРекомендаций: сгенерироватьРекомендации(отчетСравнения)
ГенераторРекомендаций --> ВебПортал: рекомендации
ВебПортал --> ОблачныйИнженер: отобразить(отчетСравнения, рекомендации)
@enduml
\ No newline at end of file
@startuml StateDiagram
title Диаграмма состояний процесса тестирования Serverless
[*] --> Создано
Создано --> Оптимизировано : оптимизировать()
Оптимизировано --> Выполняется : выполнить()
Выполняется --> МетрикиСобраны : собратьМетрики()
МетрикиСобраны --> Сравнение : сравнить()
Сравнение --> Рекомендации : сгенерироватьРекомендации()
Рекомендации --> Просмотр : просмотреть()
Просмотр --> [*]
@enduml
\ No newline at end of file
@startuml UseCase
title Диаграмма вариантов использования комплекса тестирования Serverless
left to right direction
actor ОблачныйИнженер as "Облачный инженер"
rectangle "Комплекс тестирования Serverless" {
(Настроить тестовые сценарии) as UC1
(Запустить тесты) as UC2
(Просмотреть результаты) as UC3
(Сгенерировать рекомендации) as UC4
}
ОблачныйИнженер --> UC1
ОблачныйИнженер --> UC2
ОблачныйИнженер --> UC3
ОблачныйИнженер --> UC4
@enduml
\ No newline at end of file
node_modules
npm-debug.log
build
.dockerignore
Dockerfile
.git
.github
.gitignore
README.md
docker-compose.yml
**/node_modules
**/npm-debug.log
**/.git
**/.DS_Store
\ No newline at end of file
# Stage 1: Build the React application
FROM node:20-alpine as build
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy all files
COPY . .
# Build the application
RUN npm run build
# Stage 2: Serve the built application
FROM nginx:alpine
# Copy built application from Stage 1
COPY --from=build /app/build /usr/share/nginx/html
# Copy custom nginx configuration if needed
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
version: '3.8'
services:
frontend:
build: .
ports:
- "8080:80"
restart: unless-stopped
networks:
- serverless-net
networks:
serverless-net:
driver: bridge
\ No newline at end of file
{
"name": "serverless-testing-platform",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.13.2",
"chart.js": "^4.3.0",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Платформа для тестирования serverless-функций в облачной инфраструктуре импортозамещения"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<title>Платформа тестирования Serverless</title>
</head>
<body>
<noscript>Для работы приложения необходимо включить JavaScript.</noscript>
<div id="root"></div>
</body>
</html>
\ No newline at end of file
{
"short_name": "ServerlessTest",
"name": "Платформа тестирования Serverless",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#1976d2",
"background_color": "#ffffff"
}
\ No newline at end of file
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Layout from './components/Layout';
import HomePage from './pages/HomePage';
import TestingPage from './pages/TestingPage';
import ResultsPage from './pages/ResultsPage';
import ConfigurationPage from './pages/ConfigurationPage';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
background: {
default: '#f5f5f5',
},
},
typography: {
fontFamily: 'Roboto, Arial, sans-serif',
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/testing" element={<TestingPage />} />
<Route path="/results" element={<ResultsPage />} />
<Route path="/configuration" element={<ConfigurationPage />} />
</Routes>
</Layout>
</ThemeProvider>
);
}
export default App;
\ No newline at end of file
import React, { useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import {
AppBar,
Box,
Drawer,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Typography,
useMediaQuery,
useTheme,
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import HomeIcon from '@mui/icons-material/Home';
import SpeedIcon from '@mui/icons-material/Speed';
import BarChartIcon from '@mui/icons-material/BarChart';
import SettingsIcon from '@mui/icons-material/Settings';
const drawerWidth = 240;
const menuItems = [
{ text: 'Главная', icon: <HomeIcon />, path: '/' },
{ text: 'Тестирование', icon: <SpeedIcon />, path: '/testing' },
{ text: 'Результаты', icon: <BarChartIcon />, path: '/results' },
{ text: 'Конфигурация', icon: <SettingsIcon />, path: '/configuration' },
];
function Layout({ children }) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [mobileOpen, setMobileOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<div>
<Toolbar sx={{ justifyContent: 'center' }}>
<Typography variant="h6" component="div" fontWeight="bold">
ServerlessTest
</Typography>
</Toolbar>
<List>
{menuItems.map((item) => (
<ListItem
button
key={item.text}
onClick={() => navigate(item.path)}
selected={location.pathname === item.path}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
</ListItem>
))}
</List>
</div>
);
return (
<Box sx={{ display: 'flex' }}>
<AppBar
position="fixed"
sx={{
width: { sm: `calc(100% - ${drawerWidth}px)` },
ml: { sm: `${drawerWidth}px` },
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: 'none' } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Тестирование Serverless-функций
</Typography>
</Toolbar>
</AppBar>
<Box
component="nav"
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
>
<Drawer
variant={isMobile ? 'temporary' : 'permanent'}
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true,
}}
sx={{
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
>
{drawer}
</Drawer>
</Box>
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - ${drawerWidth}px)` },
}}
>
<Toolbar />
{children}
</Box>
</Box>
);
}
export default Layout;
\ No newline at end of file
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.dashboard-card {
transition: all 0.3s;
}
.dashboard-card:hover {
transform: translateY(-5px);
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1);
}
.chart-container {
height: 300px;
position: relative;
}
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
\ No newline at end of file
import React, { useState } from 'react';
import {
Box,
Button,
Card,
CardContent,
Container,
Divider,
FormControl,
FormControlLabel,
FormGroup,
FormLabel,
Grid,
InputLabel,
MenuItem,
Paper,
Select,
Slider,
Switch,
TextField,
Typography,
Alert,
Snackbar,
} from '@mui/material';
import SaveIcon from '@mui/icons-material/Save';
import RestartAltIcon from '@mui/icons-material/RestartAlt';
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
function ConfigurationPage() {
const [cloudProvider, setCloudProvider] = useState('yandex');
const [testScenario, setTestScenario] = useState('all');
const [memoryLimit, setMemoryLimit] = useState(512);
const [iterationsCount, setIterationsCount] = useState(100);
const [concurrentRequests, setConcurrentRequests] = useState(10);
const [timeoutSeconds, setTimeoutSeconds] = useState(60);
const [saveSnackbarOpen, setSaveSnackbarOpen] = useState(false);
// Оптимизации
const [optimizations, setOptimizations] = useState({
caching: true,
packageOptimization: true,
prewarming: true,
connectionPooling: true,
streamProcessing: false,
asyncProcessing: false,
compressionOptimization: true
});
const handleOptimizationChange = (event) => {
setOptimizations({
...optimizations,
[event.target.name]: event.target.checked,
});
};
const handleSaveSettings = () => {
setSaveSnackbarOpen(true);
};
const handleCloseSnackbar = () => {
setSaveSnackbarOpen(false);
};
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Конфигурация тестирования
</Typography>
<Typography variant="body1" paragraph>
Настройте параметры тестирования и оптимизации serverless-функций.
</Typography>
<Grid container spacing={4}>
<Grid item xs={12} md={6} sx={{ display: 'flex', flexDirection: 'column' }}>
<Paper sx={{ p: 3, mb: 3, display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<Typography variant="h6" gutterBottom>
Основные параметры
</Typography>
<FormControl fullWidth margin="normal">
<InputLabel>Облачный провайдер</InputLabel>
<Select
value={cloudProvider}
label="Облачный провайдер"
onChange={(e) => setCloudProvider(e.target.value)}
>
<MenuItem value="yandex">Яндекс Облако</MenuItem>
<MenuItem value="sber">SberCloud</MenuItem>
<MenuItem value="mail">Mail.ru Cloud Solutions</MenuItem>
<MenuItem value="selectel">Selectel</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth margin="normal">
<InputLabel>Сценарий тестирования</InputLabel>
<Select
value={testScenario}
label="Сценарий тестирования"
onChange={(e) => setTestScenario(e.target.value)}
>
<MenuItem value="all">Все сценарии</MenuItem>
<MenuItem value="api">API Gateway</MenuItem>
<MenuItem value="processing">Обработка данных</MenuItem>
<MenuItem value="database">Работа с БД</MenuItem>
<MenuItem value="images">Обработка изображений</MenuItem>
</Select>
</FormControl>
<Box sx={{ mt: 3 }}>
<Typography gutterBottom>
Лимит памяти (МБ): {memoryLimit}
</Typography>
<Slider
value={memoryLimit}
onChange={(e, newValue) => setMemoryLimit(newValue)}
min={128}
max={2048}
step={128}
valueLabelDisplay="auto"
marks={[
{ value: 128, label: '128' },
{ value: 512, label: '512' },
{ value: 1024, label: '1024' },
{ value: 2048, label: '2048' },
]}
/>
</Box>
<Grid container spacing={2} sx={{ mt: 1 }}>
<Grid item xs={6}>
<TextField
fullWidth
label="Кол-во итераций"
type="number"
value={iterationsCount}
onChange={(e) => setIterationsCount(Number(e.target.value))}
/>
</Grid>
<Grid item xs={6}>
<TextField
fullWidth
label="Одновременные запросы"
type="number"
value={concurrentRequests}
onChange={(e) => setConcurrentRequests(Number(e.target.value))}
/>
</Grid>
</Grid>
<TextField
fullWidth
margin="normal"
label="Таймаут (секунды)"
type="number"
value={timeoutSeconds}
onChange={(e) => setTimeoutSeconds(Number(e.target.value))}
/>
</Paper>
<Paper sx={{ p: 3, display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<Typography variant="h6" gutterBottom>
Дополнительные параметры
</Typography>
<FormControl fullWidth margin="normal">
<TextField
fullWidth
label="Путь к тестовым данным"
defaultValue="/data/test_payload.json"
/>
</FormControl>
<FormControl fullWidth margin="normal">
<TextField
fullWidth
label="Директория для выгрузки результатов"
defaultValue="/reports/"
/>
</FormControl>
<FormControl fullWidth margin="normal">
<TextField
fullWidth
label="Имя файла отчёта"
defaultValue="serverless_test_report"
/>
</FormControl>
<Box sx={{ flexGrow: 1 }} />
</Paper>
</Grid>
<Grid item xs={12} md={6} sx={{ display: 'flex', flexDirection: 'column' }}>
<Paper sx={{ p: 3, mb: 3, display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<Typography variant="h6" gutterBottom>
Настройки оптимизации
</Typography>
<Typography variant="body2" paragraph>
Выберите оптимизации, которые система будет применять к функциям.
</Typography>
<FormControl component="fieldset" sx={{ mt: 2 }}>
<FormLabel component="legend">Оптимизации производительности</FormLabel>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={optimizations.caching}
onChange={handleOptimizationChange}
name="caching"
/>
}
label="Кэширование результатов"
/>
<FormControlLabel
control={
<Switch
checked={optimizations.packageOptimization}
onChange={handleOptimizationChange}
name="packageOptimization"
/>
}
label="Оптимизация пакета зависимостей"
/>
<FormControlLabel
control={
<Switch
checked={optimizations.prewarming}
onChange={handleOptimizationChange}
name="prewarming"
/>
}
label="Предварительный разогрев функций"
/>
<FormControlLabel
control={
<Switch
checked={optimizations.connectionPooling}
onChange={handleOptimizationChange}
name="connectionPooling"
/>
}
label="Пулинг соединений"
/>
</FormGroup>
</FormControl>
<Divider sx={{ my: 2 }} />
<FormControl component="fieldset">
<FormLabel component="legend">Оптимизации обработки данных</FormLabel>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={optimizations.streamProcessing}
onChange={handleOptimizationChange}
name="streamProcessing"
/>
}
label="Потоковая обработка данных"
/>
<FormControlLabel
control={
<Switch
checked={optimizations.asyncProcessing}
onChange={handleOptimizationChange}
name="asyncProcessing"
/>
}
label="Асинхронная обработка запросов"
/>
<FormControlLabel
control={
<Switch
checked={optimizations.compressionOptimization}
onChange={handleOptimizationChange}
name="compressionOptimization"
/>
}
label="Оптимизация сжатия данных"
/>
</FormGroup>
</FormControl>
<Box sx={{ flexGrow: 1 }} />
</Paper>
<Card sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Шаблоны оптимизации
</Typography>
<Typography variant="body2" paragraph>
Выберите готовый шаблон оптимизации или сохраните текущие настройки как шаблон.
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button
variant="outlined"
startIcon={<SettingsBackupRestoreIcon />}
onClick={() => {
setOptimizations({
caching: true,
packageOptimization: true,
prewarming: true,
connectionPooling: true,
streamProcessing: false,
asyncProcessing: false,
compressionOptimization: true,
});
}}
>
Загрузить базовые оптимизации
</Button>
<Button
variant="outlined"
startIcon={<SettingsBackupRestoreIcon />}
onClick={() => {
setOptimizations({
caching: true,
packageOptimization: true,
prewarming: true,
connectionPooling: true,
streamProcessing: true,
asyncProcessing: true,
compressionOptimization: true,
});
}}
>
Загрузить максимальные оптимизации
</Button>
<Button
variant="outlined"
startIcon={<SaveIcon />}
>
Сохранить текущие настройки как шаблон
</Button>
</Box>
<Box sx={{ flexGrow: 1 }} />
</CardContent>
</Card>
</Grid>
</Grid>
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'flex-end', gap: 2 }}>
<Button
variant="outlined"
startIcon={<RestartAltIcon />}
onClick={() => {
setCloudProvider('yandex');
setTestScenario('all');
setMemoryLimit(512);
setIterationsCount(100);
setConcurrentRequests(10);
setTimeoutSeconds(60);
setOptimizations({
caching: true,
packageOptimization: true,
prewarming: true,
connectionPooling: true,
streamProcessing: false,
asyncProcessing: false,
compressionOptimization: true,
});
}}
>
Сбросить настройки
</Button>
<Button
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSaveSettings}
>
Сохранить настройки
</Button>
</Box>
<Snackbar
open={saveSnackbarOpen}
autoHideDuration={6000}
onClose={handleCloseSnackbar}
>
<Alert
onClose={handleCloseSnackbar}
severity="success"
variant="filled"
>
Настройки успешно сохранены!
</Alert>
</Snackbar>
</Box>
</Container>
);
}
export default ConfigurationPage;
\ No newline at end of file
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
Box,
Button,
Card,
CardActionArea,
CardContent,
Container,
Grid,
Typography,
Paper,
} from '@mui/material';
import SpeedIcon from '@mui/icons-material/Speed';
import MemoryIcon from '@mui/icons-material/Memory';
import StorageIcon from '@mui/icons-material/Storage';
import TimelineIcon from '@mui/icons-material/Timeline';
const performanceData = [
{ title: 'Время ответа', icon: <TimelineIcon fontSize="large" color="primary" />, value: '124 мс', change: '-23%' },
{ title: 'Использование CPU', icon: <SpeedIcon fontSize="large" color="primary" />, value: '32%', change: '-12%' },
{ title: 'Использование RAM', icon: <MemoryIcon fontSize="large" color="primary" />, value: '256 MB', change: '-18%' },
{ title: 'Хранилище', icon: <StorageIcon fontSize="large" color="primary" />, value: '45 MB', change: '-8%' },
];
function HomePage() {
const navigate = useNavigate();
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Тестирование и оптимизация Serverless-функций
</Typography>
<Typography variant="body1" paragraph>
Добро пожаловать в платформу для автоматизированного запуска и замера производительности
serverless-функций на облачной инфраструктуре импортозамещения.
</Typography>
<Paper sx={{ p: 3, mb: 4, bgcolor: 'primary.main', color: 'white' }}>
<Typography variant="h5" gutterBottom>
Текущий статус:
</Typography>
<Typography variant="body1">
Проведено 153 тестов Оптимизировано 27 функций Среднее улучшение: 24%
</Typography>
<Button
variant="contained"
color="secondary"
sx={{ mt: 2 }}
onClick={() => navigate('/testing')}
>
Запустить новый тест
</Button>
</Paper>
<Typography variant="h5" gutterBottom>
Сводка производительности
</Typography>
<Grid container spacing={3}>
{performanceData.map((item) => (
<Grid item xs={12} sm={6} md={3} key={item.title} sx={{ display: 'flex' }}>
<Card className="dashboard-card" sx={{ width: '100%', height: '100%' }}>
<CardActionArea sx={{ height: '100%' }} onClick={() => navigate('/results')}>
<CardContent sx={{ textAlign: 'center', display: 'flex', flexDirection: 'column', height: '100%' }}>
<Box sx={{ mb: 2 }}>{item.icon}</Box>
<Typography variant="h6" component="div">
{item.title}
</Typography>
<Typography variant="h4" color="text.primary" sx={{ mt: 1 }}>
{item.value}
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Typography
variant="body2"
color={item.change.startsWith('-') ? 'success.main' : 'error.main'}
sx={{ mt: 2 }}
>
{item.change} после оптимизации
</Typography>
</CardContent>
</CardActionArea>
</Card>
</Grid>
))}
</Grid>
<Grid container spacing={4} sx={{ mt: 2 }}>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Последние тесты
</Typography>
<Typography variant="body2">
Тест обработки изображений (завершен 12.05.2024)
</Typography>
<Typography variant="body2">
Тест API Gateway с аутентификацией (завершен 10.05.2024)
</Typography>
<Typography variant="body2">
Тест базы данных PostgreSQL (завершен 08.05.2024)
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
size="small"
sx={{ mt: 2 }}
onClick={() => navigate('/results')}
>
Все результаты
</Button>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Рекомендации
</Typography>
<Typography variant="body2">
Оптимизировать использование памяти в функции обработки изображений
</Typography>
<Typography variant="body2">
Кэшировать результаты HTTP-запросов для улучшения времени отклика
</Typography>
<Typography variant="body2">
Уменьшить размер пакета зависимостей для функции аналитики
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
size="small"
sx={{ mt: 2 }}
onClick={() => navigate('/configuration')}
>
Настроить оптимизации
</Button>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default HomePage;
\ No newline at end of file
import React, { useState } from 'react';
import {
Box,
Button,
Card,
CardContent,
Container,
FormControl,
Grid,
InputLabel,
MenuItem,
Paper,
Select,
Tab,
Tabs,
Typography,
} from '@mui/material';
import FileDownloadIcon from '@mui/icons-material/FileDownload';
import { Line, Bar } from 'react-chartjs-2';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
// Регистрация компонентов ChartJS
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
Title,
Tooltip,
Legend
);
// Данные о времени отклика (мс)
const responseTimeData = {
labels: ['0', '10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
datasets: [
{
label: 'До оптимизации',
data: [165, 170, 172, 168, 182, 190, 178, 175, 172, 178, 185],
borderColor: '#f44336',
backgroundColor: 'rgba(244, 67, 54, 0.1)',
tension: 0.4,
},
{
label: 'После оптимизации',
data: [132, 128, 125, 130, 133, 140, 138, 135, 130, 128, 124],
borderColor: '#4caf50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
tension: 0.4,
},
],
};
// Данные об использовании ресурсов
const resourceUsageData = {
labels: ['ЦП', 'Память', 'Сеть', 'Хранилище'],
datasets: [
{
label: 'До оптимизации',
data: [48, 320, 75, 55],
backgroundColor: 'rgba(244, 67, 54, 0.7)',
},
{
label: 'После оптимизации',
data: [32, 256, 60, 45],
backgroundColor: 'rgba(76, 175, 80, 0.7)',
},
],
};
// Данные о холодном старте (мс)
const coldStartData = {
labels: ['API Gateway', 'Обработка данных', 'Работа с БД', 'Обработка изображений'],
datasets: [
{
label: 'До оптимизации',
data: [420, 380, 350, 480],
backgroundColor: 'rgba(244, 67, 54, 0.7)',
},
{
label: 'После оптимизации',
data: [320, 280, 260, 350],
backgroundColor: 'rgba(76, 175, 80, 0.7)',
},
],
};
// Данные о пропускной способности (запросов/сек)
const throughputData = {
labels: ['0', '10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
datasets: [
{
label: 'До оптимизации',
data: [60, 62, 58, 55, 57, 52, 50, 48, 45, 43, 40],
borderColor: '#f44336',
backgroundColor: 'rgba(244, 67, 54, 0.1)',
tension: 0.4,
},
{
label: 'После оптимизации',
data: [70, 75, 78, 82, 85, 83, 80, 78, 75, 72, 70],
borderColor: '#4caf50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
tension: 0.4,
},
],
};
// Общие опции для графиков
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
},
};
function ResultsPage() {
const [tabValue, setTabValue] = useState(0);
const [platform, setPlatform] = useState('yandex');
const [timeRange, setTimeRange] = useState('week');
const handleTabChange = (event, newValue) => {
setTabValue(newValue);
};
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Результаты тестирования
</Typography>
<Typography variant="body1" paragraph>
Сравнительный анализ производительности serverless-функций до и после оптимизации.
</Typography>
<Grid container spacing={3} sx={{ mb: 4 }}>
<Grid item xs={12} md={6}>
<FormControl fullWidth>
<InputLabel>Облачная платформа</InputLabel>
<Select
value={platform}
label="Облачная платформа"
onChange={(e) => setPlatform(e.target.value)}
>
<MenuItem value="yandex">Яндекс Облако</MenuItem>
<MenuItem value="sber">SberCloud</MenuItem>
<MenuItem value="mail">Mail.ru Cloud Solutions</MenuItem>
<MenuItem value="selectel">Selectel</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl fullWidth>
<InputLabel>Временной диапазон</InputLabel>
<Select
value={timeRange}
label="Временной диапазон"
onChange={(e) => setTimeRange(e.target.value)}
>
<MenuItem value="day">Последний день</MenuItem>
<MenuItem value="week">Последняя неделя</MenuItem>
<MenuItem value="month">Последний месяц</MenuItem>
<MenuItem value="custom">Пользовательский</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
<Paper sx={{ mb: 4 }}>
<Tabs
value={tabValue}
onChange={handleTabChange}
sx={{ borderBottom: 1, borderColor: 'divider' }}
centered
>
<Tab label="Время отклика" />
<Tab label="Использование ресурсов" />
<Tab label="Холодный старт" />
<Tab label="Пропускная способность" />
</Tabs>
<Box sx={{ p: 3 }}>
{tabValue === 0 && (
<Box>
<Typography variant="h6" gutterBottom>
Время отклика (мс) при различных уровнях нагрузки
</Typography>
<Typography variant="body2" paragraph>
График показывает сравнение времени отклика до и после оптимизации при увеличении
нагрузки от 0 до 100%. Наблюдается снижение времени отклика на 23% после оптимизации.
</Typography>
<Box sx={{ height: 400 }}>
<Line options={chartOptions} data={responseTimeData} />
</Box>
</Box>
)}
{tabValue === 1 && (
<Box>
<Typography variant="h6" gutterBottom>
Использование ресурсов
</Typography>
<Typography variant="body2" paragraph>
График сравнивает использование различных ресурсов до и после оптимизации.
Значения для ЦП указаны в процентах, для памяти - в МБ, для сети и хранилища - в МБ/сек.
</Typography>
<Box sx={{ height: 400 }}>
<Bar options={chartOptions} data={resourceUsageData} />
</Box>
</Box>
)}
{tabValue === 2 && (
<Box>
<Typography variant="h6" gutterBottom>
Время холодного старта (мс)
</Typography>
<Typography variant="body2" paragraph>
График показывает время холодного старта для различных типов функций до и после оптимизации.
Среднее снижение составило 27%.
</Typography>
<Box sx={{ height: 400 }}>
<Bar options={chartOptions} data={coldStartData} />
</Box>
</Box>
)}
{tabValue === 3 && (
<Box>
<Typography variant="h6" gutterBottom>
Пропускная способность (запросов/сек)
</Typography>
<Typography variant="body2" paragraph>
График показывает сравнение пропускной способности до и после оптимизации при увеличении
нагрузки от 0 до 100%. Наблюдается увеличение пропускной способности на 65%.
</Typography>
<Box sx={{ height: 400 }}>
<Line options={chartOptions} data={throughputData} />
</Box>
</Box>
)}
</Box>
</Paper>
<Grid container spacing={3}>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Сводка улучшений
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2">Время отклика:</Typography>
<Typography variant="body2" color="success.main">-23%</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2">Использование ЦП:</Typography>
<Typography variant="body2" color="success.main">-33%</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2">Использование памяти:</Typography>
<Typography variant="body2" color="success.main">-20%</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2">Время холодного старта:</Typography>
<Typography variant="body2" color="success.main">-27%</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="body2">Пропускная способность:</Typography>
<Typography variant="body2" color="success.main">+65%</Typography>
</Box>
<Box sx={{ flexGrow: 1 }} />
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Детали теста
</Typography>
<Typography variant="body2">
<strong>Платформа:</strong> Яндекс Облако
</Typography>
<Typography variant="body2">
<strong>Сценарий:</strong> API Gateway
</Typography>
<Typography variant="body2">
<strong>Дата запуска:</strong> 12.05.2024
</Typography>
<Typography variant="body2">
<strong>Кол-во итераций:</strong> 100
</Typography>
<Typography variant="body2">
<strong>Одновременных запросов:</strong> 10
</Typography>
<Typography variant="body2">
<strong>Примененные оптимизации:</strong> Кэширование, оптимизация пакета зависимостей, предварительный разогрев
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
startIcon={<FileDownloadIcon />}
sx={{ mt: 2 }}
>
Экспорт отчета
</Button>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default ResultsPage;
\ No newline at end of file
import React, { useState } from 'react';
import {
Box,
Button,
Card,
CardContent,
Chip,
Container,
Divider,
FormControl,
Grid,
InputLabel,
LinearProgress,
MenuItem,
Paper,
Select,
Stack,
Step,
StepLabel,
Stepper,
Switch,
TextField,
Typography,
FormControlLabel,
} from '@mui/material';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import CodeIcon from '@mui/icons-material/Code';
import CloudIcon from '@mui/icons-material/Cloud';
import SettingsIcon from '@mui/icons-material/Settings';
import BugReportIcon from '@mui/icons-material/BugReport';
const testSteps = ['Настройка теста', 'Запуск тестов', 'Анализ результатов', 'Формирование рекомендаций'];
const cloudOptions = [
{ value: 'yandex', label: 'Яндекс Облако' },
{ value: 'sber', label: 'SberCloud' },
{ value: 'mail', label: 'Mail.ru Cloud Solutions' },
{ value: 'selectel', label: 'Selectel' },
];
const scenarioOptions = [
{ value: 'api', label: 'API Gateway' },
{ value: 'processing', label: 'Обработка данных' },
{ value: 'database', label: 'Работа с БД' },
{ value: 'images', label: 'Обработка изображений' },
{ value: 'custom', label: 'Пользовательский сценарий' },
];
function TestingPage() {
const [activeStep, setActiveStep] = useState(0);
const [cloud, setCloud] = useState('yandex');
const [scenario, setScenario] = useState('api');
const [iterations, setIterations] = useState(100);
const [concurrency, setConcurrency] = useState(10);
const [optimizationEnabled, setOptimizationEnabled] = useState(true);
const [testRunning, setTestRunning] = useState(false);
const [progress, setProgress] = useState(0);
const handleStartTest = () => {
setTestRunning(true);
setActiveStep(1);
// Симуляция прогресса
const interval = setInterval(() => {
setProgress((prevProgress) => {
const newProgress = prevProgress + 5;
if (newProgress >= 100) {
clearInterval(interval);
setTestRunning(false);
setActiveStep(2);
return 100;
}
return newProgress;
});
}, 500);
};
const handleReset = () => {
setActiveStep(0);
setProgress(0);
setTestRunning(false);
};
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Тестирование Serverless-функций
</Typography>
<Typography variant="body1" paragraph>
Настройте параметры и запустите тестирование функций на выбранной облачной платформе.
</Typography>
<Paper sx={{ p: 3, mb: 4 }}>
<Stepper activeStep={activeStep} sx={{ mb: 4 }}>
{testSteps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
{activeStep === 0 && (
<Box>
<Typography variant="h6" gutterBottom>
Параметры тестирования
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<FormControl fullWidth margin="normal">
<InputLabel>Облачная платформа</InputLabel>
<Select
value={cloud}
label="Облачная платформа"
onChange={(e) => setCloud(e.target.value)}
>
{cloudOptions.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl fullWidth margin="normal">
<InputLabel>Сценарий тестирования</InputLabel>
<Select
value={scenario}
label="Сценарий тестирования"
onChange={(e) => setScenario(e.target.value)}
>
{scenarioOptions.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<Grid container spacing={2} sx={{ mt: 1 }}>
<Grid item xs={6}>
<TextField
fullWidth
label="Кол-во итераций"
type="number"
value={iterations}
onChange={(e) => setIterations(Number(e.target.value))}
/>
</Grid>
<Grid item xs={6}>
<TextField
fullWidth
label="Одновременные запросы"
type="number"
value={concurrency}
onChange={(e) => setConcurrency(Number(e.target.value))}
/>
</Grid>
</Grid>
<FormControlLabel
control={
<Switch
checked={optimizationEnabled}
onChange={(e) => setOptimizationEnabled(e.target.checked)}
/>
}
label="Применить оптимизацию"
sx={{ mt: 2 }}
/>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ mb: 2, bgcolor: '#f5f5f5' }}>
<CardContent>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }}>
<CloudIcon color="primary" />
<Typography variant="subtitle1">
Информация о платформе
</Typography>
</Stack>
<Typography variant="body2">
Яндекс Облако - российская облачная платформа, предоставляющая
serverless-решения на базе отечественных технологий.
</Typography>
<Divider sx={{ my: 2 }} />
<Stack direction="row" spacing={1}>
<Chip label="4 региона" size="small" />
<Chip label="Python 3.10" size="small" />
<Chip label="Node.js 16" size="small" />
</Stack>
</CardContent>
</Card>
<Card sx={{ bgcolor: '#f5f5f5' }}>
<CardContent>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }}>
<CodeIcon color="primary" />
<Typography variant="subtitle1">
Сценарий тестирования
</Typography>
</Stack>
<Typography variant="body2">
API Gateway - тестирование производительности функций,
обрабатывающих HTTP-запросы через API Gateway. Измеряется время
отклика, пропускная способность и стабильность работы.
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="contained"
startIcon={<PlayArrowIcon />}
onClick={handleStartTest}
disabled={testRunning}
>
Запустить тестирование
</Button>
</Box>
</Box>
)}
{activeStep === 1 && (
<Box>
<Typography variant="h6" gutterBottom>
Выполнение тестов
</Typography>
<LinearProgress variant="determinate" value={progress} sx={{ height: 10, mb: 2 }} />
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
Прогресс: {progress}% ({Math.floor(iterations * progress / 100)} из {iterations} итераций)
</Typography>
<Paper variant="outlined" sx={{ p: 2, mb: 3, bgcolor: '#f8f8f8' }}>
<Typography variant="subtitle2" gutterBottom>
Лог выполнения:
</Typography>
<Box sx={{ maxHeight: '250px', overflow: 'auto', fontFamily: 'monospace', fontSize: '0.85rem' }}>
<Box sx={{ py: 0.5 }}>
<Typography variant="body2" component="span" color="text.secondary">[16:42:23]</Typography>
<Typography variant="body2" component="span"> Инициализация тестового окружения...</Typography>
</Box>
<Box sx={{ py: 0.5 }}>
<Typography variant="body2" component="span" color="text.secondary">[16:42:25]</Typography>
<Typography variant="body2" component="span"> Развёртывание тестовой функции в облаке Яндекс...</Typography>
</Box>
<Box sx={{ py: 0.5 }}>
<Typography variant="body2" component="span" color="text.secondary">[16:42:30]</Typography>
<Typography variant="body2" component="span"> Начало тестирования API Gateway...</Typography>
</Box>
<Box sx={{ py: 0.5 }}>
<Typography variant="body2" component="span" color="text.secondary">[16:42:35]</Typography>
<Typography variant="body2" component="span"> Выполнено 25 из {iterations} итераций...</Typography>
</Box>
<Box sx={{ py: 0.5 }}>
<Typography variant="body2" component="span" color="text.secondary">[16:42:40]</Typography>
<Typography variant="body2" component="span"> Сбор первичных метрик...</Typography>
</Box>
</Box>
</Paper>
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'space-between' }}>
<Button variant="outlined" onClick={handleReset} disabled={progress < 100 && progress > 0}>
Отменить
</Button>
<Button variant="contained" disabled={progress < 100}>
Анализировать результаты
</Button>
</Box>
</Box>
)}
{activeStep === 2 && (
<Box>
<Typography variant="h6" gutterBottom>
Предварительные результаты
</Typography>
<Typography variant="body2" paragraph>
Результаты тестирования доступны. Система автоматически проанализировала данные
и подготовила рекомендации по оптимизации.
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<Card sx={{ mb: 2 }}>
<CardContent>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }}>
<SettingsIcon color="primary" />
<Typography variant="subtitle1">
Метрики производительности
</Typography>
</Stack>
<Typography variant="body2">
<strong>Время ответа (P95):</strong> 124 мс
</Typography>
<Typography variant="body2">
<strong>Время холодного старта:</strong> 320 мс
</Typography>
<Typography variant="body2">
<strong>Пропускная способность:</strong> 82 TPS
</Typography>
<Typography variant="body2">
<strong>Использование ЦП:</strong> 32%
</Typography>
<Typography variant="body2">
<strong>Использование RAM:</strong> 256 МБ
</Typography>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} sm={6}>
<Card>
<CardContent>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }}>
<BugReportIcon color="primary" />
<Typography variant="subtitle1">
Выявленные проблемы
</Typography>
</Stack>
<Typography variant="body2" sx={{ mb: 1 }}>
Высокое время холодного старта
</Typography>
<Typography variant="body2" sx={{ mb: 1 }}>
Избыточное потребление памяти
</Typography>
<Typography variant="body2" sx={{ mb: 1 }}>
Неоптимальные HTTP-заголовки кэширования
</Typography>
<Typography variant="body2">
Большой размер пакета развертывания
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'space-between' }}>
<Button variant="outlined" onClick={handleReset}>
Новый тест
</Button>
<Button
variant="contained"
onClick={() => setActiveStep(3)}
>
Просмотреть рекомендации
</Button>
</Box>
</Box>
)}
{activeStep === 3 && (
<Box>
<Typography variant="h6" gutterBottom>
Рекомендации по оптимизации
</Typography>
<Typography variant="body2" paragraph>
На основе результатов тестирования система сформировала следующие рекомендации
для оптимизации производительности функций.
</Typography>
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="subtitle1" gutterBottom>
1. Оптимизация холодного старта
</Typography>
<Typography variant="body2" paragraph>
Время холодного старта составляет 320 мс, что превышает оптимальное значение.
Рекомендуется:
</Typography>
<Typography variant="body2" component="ul">
<li>Использовать функциональность предварительного разогрева</li>
<li>Уменьшить размер пакета развертывания с 45 МБ до 20 МБ</li>
<li>Оптимизировать импорты, исключив неиспользуемые зависимости</li>
<li>Внедрить механизм кэширования соединений с внешними сервисами</li>
</Typography>
</CardContent>
</Card>
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="subtitle1" gutterBottom>
2. Оптимизация использования памяти
</Typography>
<Typography variant="body2" paragraph>
Использование оперативной памяти составляет 256 МБ при пиковой нагрузке.
Рекомендуется:
</Typography>
<Typography variant="body2" component="ul">
<li>Оптимизировать алгоритмы обработки данных для снижения потребления памяти</li>
<li>Внедрить потоковую обработку вместо загрузки всего набора данных в память</li>
<li>Настроить конфигурацию выделения памяти в соответствии с требованиями функции</li>
</Typography>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="subtitle1" gutterBottom>
3. Оптимизация HTTP-взаимодействия
</Typography>
<Typography variant="body2" paragraph>
Время ответа составляет 124 мс (P95). Рекомендуется:
</Typography>
<Typography variant="body2" component="ul">
<li>Настроить правильные заголовки кэширования для статичных ответов</li>
<li>Использовать оптимизацию сжатия gzip/brotli для уменьшения объема передаваемых данных</li>
<li>Разделить функцию на несколько более специализированных для улучшения масштабирования</li>
</Typography>
</CardContent>
</Card>
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'space-between' }}>
<Button variant="outlined" onClick={handleReset}>
Новый тест
</Button>
<Button
variant="contained"
onClick={() => console.log('Применение оптимизаций')}
>
Применить оптимизации
</Button>
</Box>
</Box>
)}
</Paper>
</Box>
</Container>
);
}
export default TestingPage;
\ No newline at end of file
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment