mirror of https://github.com/ivanch/tcc.git
Compare commits
No commits in common. "6d8ec28dbb8628d1ec96d3791203d42be8d32626" and "aa4b1e9709ef74e88d091e40336e16886bcdf414" have entirely different histories.
6d8ec28dbb
...
aa4b1e9709
|
@ -1,2 +0,0 @@
|
||||||
Dockerfile
|
|
||||||
target/
|
|
|
@ -9,7 +9,6 @@ dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"magick_rust",
|
"magick_rust",
|
||||||
"qstring",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -300,9 +299,9 @@ checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.68.1"
|
version = "0.66.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
|
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
|
@ -786,9 +785,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "magick_rust"
|
name = "magick_rust"
|
||||||
version = "0.19.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c912b69250bcd5d024852a1a75c567d3b5d881871a55b741018741632a921bf8"
|
checksum = "ce666df6ace809001c625ad383f4727657c7c7881df43b3b71486e8e9f07d017"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -953,15 +952,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qstring"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e"
|
|
||||||
dependencies = [
|
|
||||||
"percent-encoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.33"
|
||||||
|
|
|
@ -8,5 +8,4 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "4"
|
actix-web = "4"
|
||||||
actix-files = "0.6.2"
|
actix-files = "0.6.2"
|
||||||
magick_rust = "0.19.1"
|
magick_rust = "0.19.0"
|
||||||
qstring = "0.7.2"
|
|
||||||
|
|
|
@ -1,34 +1,25 @@
|
||||||
FROM rust:latest
|
FROM rust:slim-bullseye AS build-env
|
||||||
|
|
||||||
ENV MAGICK_VERSION 7.1
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get -y install wget curl build-essential clang pkg-config libjpeg-turbo-progs libpng-dev
|
|
||||||
|
|
||||||
RUN apt update && apt install curl -y \
|
|
||||||
&& curl https://imagemagick.org/archive/ImageMagick.tar.gz | tar xz \
|
|
||||||
&& cd ImageMagick-${MAGICK_VERSION}* \
|
|
||||||
&& ./configure --with-magick-plus-plus=no --with-perl=no \
|
|
||||||
&& make \
|
|
||||||
&& make install \
|
|
||||||
&& cd .. \
|
|
||||||
&& rm -r ImageMagick-${MAGICK_VERSION}*
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN wget https://files.ivanch.me/api/public/dl/iFuXSNhw/small-image.png && \
|
|
||||||
wget https://files.ivanch.me/api/public/dl/81Bkht5C/big-image.png && \
|
|
||||||
wget https://files.ivanch.me/api/public/dl/nAndfAjK/video.mp4
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN cargo build --release && \
|
RUN apt update && apt install wget -y && \
|
||||||
cp ./target/release/ActixAPI . && \
|
wget https://files.ivanch.me/api/public/dl/iFuXSNhw/small-image.png && \
|
||||||
|
wget https://files.ivanch.me/api/public/dl/81Bkht5C/big-image.png && \
|
||||||
|
wget https://files.ivanch.me/api/public/dl/nAndfAjK/video.mp4 && \
|
||||||
mv small-image.png ./static && \
|
mv small-image.png ./static && \
|
||||||
mv big-image.png ./static && \
|
mv big-image.png ./static && \
|
||||||
mv video.mp4 ./static && \
|
mv video.mp4 ./static
|
||||||
ldconfig /usr/local/lib
|
|
||||||
|
|
||||||
ENV LD_LIBRARY_PATH=/usr/local/lib
|
RUN cargo build --release
|
||||||
|
|
||||||
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build-env /app/target/release .
|
||||||
|
|
||||||
|
COPY --from=build-env /app/static ./static
|
||||||
|
|
||||||
ENTRYPOINT ["./ActixAPI"]
|
ENTRYPOINT ["./ActixAPI"]
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
use qstring::QString;
|
|
||||||
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, HttpRequest, Result};
|
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, HttpRequest, Result};
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use magick_rust::MagickWand;
|
use magick_rust::{MagickWand, ToMagick};
|
||||||
|
use magick_rust::{self, FilterType};
|
||||||
|
|
||||||
#[get("/status/ok")]
|
#[get("/status/ok")]
|
||||||
async fn hello() -> impl Responder {
|
async fn hello() -> impl Responder {
|
||||||
HttpResponse::Ok().body("{\"status\": 200}")
|
HttpResponse::Ok().body("{\"status\": 200}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/echo")]
|
||||||
|
async fn echo(req_body: String) -> impl Responder {
|
||||||
|
HttpResponse::Ok().body(req_body)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn manual_hello() -> impl Responder {
|
||||||
|
HttpResponse::Ok().body("Hey there!")
|
||||||
|
}
|
||||||
|
|
||||||
async fn static_serve(req: HttpRequest) -> Result<NamedFile> {
|
async fn static_serve(req: HttpRequest) -> Result<NamedFile> {
|
||||||
let path: &str = req.path();
|
let path: &str = req.path();
|
||||||
let real_path = &path[1..];
|
let real_path = &path[1..];
|
||||||
|
@ -15,50 +24,19 @@ async fn static_serve(req: HttpRequest) -> Result<NamedFile> {
|
||||||
Ok(NamedFile::open(real_path)?)
|
Ok(NamedFile::open(real_path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/image/load-small-image")]
|
|
||||||
async fn load_small_image() -> Result<NamedFile> {
|
|
||||||
let real_path = "static/small-image.png";
|
|
||||||
|
|
||||||
Ok(NamedFile::open(real_path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/image/load-big-image")]
|
|
||||||
async fn load_big_image() -> Result<NamedFile> {
|
|
||||||
let real_path = "static/big-image.png";
|
|
||||||
|
|
||||||
Ok(NamedFile::open(real_path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/image/save-big-image")]
|
|
||||||
async fn save_big_image(image_data: web::Bytes) -> Result<HttpResponse> {
|
|
||||||
// Write bytes to file
|
|
||||||
std::fs::write("image.png", &image_data).unwrap();
|
|
||||||
|
|
||||||
// Return the blurred image bytes
|
|
||||||
Ok(HttpResponse::Ok()
|
|
||||||
.content_type("application/json")
|
|
||||||
.body("{\"status\": 200}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/image/blur")]
|
#[post("/image/blur")]
|
||||||
async fn blur_image(image_data: web::Bytes, req: HttpRequest) -> Result<HttpResponse> {
|
async fn blur_image(image_data: web::Bytes) -> Result<HttpResponse> {
|
||||||
// Load the image from the incoming bytes
|
// Load the image from the incoming bytes
|
||||||
let mut wand = MagickWand::new();
|
let image = ToMagick::<magick_rust::MagickWand>::to_magick(image_data).unwrap();
|
||||||
wand.read_image_blob(&image_data).unwrap();
|
|
||||||
|
|
||||||
let query_str = req.query_string();
|
// Apply a blur filter to the image
|
||||||
let qs = QString::from(query_str);
|
let blurred_image = image.blur(FilterType::Gaussian, 0.0).unwrap();
|
||||||
let radius = qs.get("radius").unwrap_or("5").parse::<f64>().unwrap_or(5.0);
|
|
||||||
|
|
||||||
// Blur the image
|
// Convert the blurred image back to bytes
|
||||||
wand.blur_image(radius, radius).unwrap();
|
let blurred_image_bytes = blurred_image.to_bytes().unwrap();
|
||||||
|
|
||||||
// Convert the image back to bytes
|
|
||||||
let blurred_image_bytes = wand.write_image_blob("png").unwrap();
|
|
||||||
|
|
||||||
// Return the blurred image bytes
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type("image/png")
|
.content_type("image/jpeg")
|
||||||
.body(blurred_image_bytes))
|
.body(blurred_image_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,13 +48,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
App::new()
|
App::new()
|
||||||
.route("/static/{filename:.*}", web::get().to(static_serve))
|
.route("/static/{filename:.*}", web::get().to(static_serve))
|
||||||
.service(hello)
|
.service(hello)
|
||||||
.service(load_small_image)
|
.service(echo)
|
||||||
.service(load_big_image)
|
|
||||||
.service(save_big_image)
|
|
||||||
.service(blur_image)
|
.service(blur_image)
|
||||||
.app_data(web::PayloadConfig::new(1024 * 1024 * 1024))
|
.route("/hey", web::get().to(manual_hello))
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 5000))?
|
.bind(("0.0.0.0", 9090))?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
|
@ -31,7 +31,7 @@ services:
|
||||||
build: ./ActixAPI
|
build: ./ActixAPI
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "9083:5000"
|
- "9083:9090"
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
|
|
|
@ -13,8 +13,6 @@ ENDPOINTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
BLUR_RADIUS = 5
|
BLUR_RADIUS = 5
|
||||||
AVG_RUNS = 3
|
|
||||||
|
|
||||||
API_REQUESTS = [
|
API_REQUESTS = [
|
||||||
('/status/ok', 'GET', range(0, 30_000, 5000), None),
|
('/status/ok', 'GET', range(0, 30_000, 5000), None),
|
||||||
('/image/save-big-image', 'POST', range(0, 10_000, 1_000), open('big-image.png', 'rb').read()),
|
('/image/save-big-image', 'POST', range(0, 10_000, 1_000), open('big-image.png', 'rb').read()),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import os
|
||||||
from common import API_REQUESTS, FRAMEWORKS
|
from common import API_REQUESTS, FRAMEWORKS
|
||||||
|
|
||||||
FRAMEWORKS = [f for f, _ in FRAMEWORKS]
|
FRAMEWORKS = [f for f, _ in FRAMEWORKS]
|
||||||
|
|
|
@ -2,10 +2,11 @@ import requests
|
||||||
import docker
|
import docker
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
from math import floor
|
from math import floor
|
||||||
from init import init
|
from init import init
|
||||||
from common import FRAMEWORKS, ENDPOINTS, API_REQUESTS, AVG_RUNS
|
from common import FRAMEWORKS, ENDPOINTS, API_REQUESTS
|
||||||
|
|
||||||
init()
|
init()
|
||||||
|
|
||||||
|
@ -61,13 +62,10 @@ def run_tests(endpoint, method, num_requests, metadata):
|
||||||
for num_request in num_requests:
|
for num_request in num_requests:
|
||||||
if num_request <= 0: continue
|
if num_request <= 0: continue
|
||||||
|
|
||||||
total_cpu, total_ram = 0, 0
|
|
||||||
total_time = 0
|
|
||||||
|
|
||||||
for run in range(AVG_RUNS):
|
|
||||||
ok_responses = 0
|
ok_responses = 0
|
||||||
bad_responses = 0
|
bad_responses = 0
|
||||||
server_errors = 0
|
server_errors = 0
|
||||||
|
cpu, ram = 0, 0
|
||||||
|
|
||||||
with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor:
|
||||||
url = f'{URL_BASE}{endpoint}'
|
url = f'{URL_BASE}{endpoint}'
|
||||||
|
@ -84,13 +82,10 @@ def run_tests(endpoint, method, num_requests, metadata):
|
||||||
|
|
||||||
if i == half:
|
if i == half:
|
||||||
cpu, ram = get_resource_usage()
|
cpu, ram = get_resource_usage()
|
||||||
total_cpu += float(cpu)
|
|
||||||
total_ram += float(ram)
|
|
||||||
|
|
||||||
concurrent.futures.wait(futures)
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
elapsed_time = time.time() - start_time
|
elapsed_time = time.time() - start_time
|
||||||
total_time += elapsed_time
|
|
||||||
|
|
||||||
for future in futures:
|
for future in futures:
|
||||||
responses = future.result()
|
responses = future.result()
|
||||||
|
@ -98,20 +93,12 @@ def run_tests(endpoint, method, num_requests, metadata):
|
||||||
bad_responses += responses[4]
|
bad_responses += responses[4]
|
||||||
server_errors += responses[5]
|
server_errors += responses[5]
|
||||||
|
|
||||||
print(f"[#{run}] {num_request}: {elapsed_time:.2f} seconds. {elapsed_time/num_request:.4f} seconds per request. {num_request/elapsed_time:.2f} requests per second. [OK: {ok_responses}, Bad Request: {bad_responses}, Server Error: {server_errors}]]")
|
print(f"{num_request}: {elapsed_time:.2f} seconds. {elapsed_time/num_request:.4f} seconds per request. {num_request/elapsed_time:.2f} requests per second. [OK: {ok_responses}, Bad Request: {bad_responses}, Server Error: {server_errors}]]")
|
||||||
|
|
||||||
client = docker.from_env()
|
|
||||||
client.containers.get(CONTAINER_NAME).restart()
|
|
||||||
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
cpu = total_cpu / AVG_RUNS
|
|
||||||
ram = total_ram / AVG_RUNS
|
|
||||||
elapsed_time = total_time / AVG_RUNS
|
|
||||||
|
|
||||||
record(files[0], num_request, f"{num_request/elapsed_time:.2f}")
|
record(files[0], num_request, f"{num_request/elapsed_time:.2f}")
|
||||||
record_resource(files[1], num_request, cpu, ram)
|
record_resource(files[1], num_request, cpu, ram)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
def get_resource_usage():
|
def get_resource_usage():
|
||||||
if CONTAINER_NAME == "": return 0, 0
|
if CONTAINER_NAME == "": return 0, 0
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 504b59278f6024219814649f2715b70561251c65
|
|
Loading…
Reference in New Issue