Compare commits

...

5 Commits

Author SHA1 Message Date
José Henrique 6d8ec28dbb save image 2023-09-28 22:53:09 -03:00
José Henrique 4535ed5cf6 fix actix docker 2023-09-28 22:35:06 -03:00
José Henrique 4fa2ae956f curl 2023-09-28 21:03:32 -03:00
José Henrique 2e3398f683 average runs 2023-09-28 21:00:55 -03:00
José Henrique 2f91c3636f add actix 2023-09-28 20:40:45 -03:00
10 changed files with 133 additions and 72 deletions

2
ActixAPI/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
Dockerfile
target/

18
ActixAPI/Cargo.lock generated
View File

@ -9,6 +9,7 @@ dependencies = [
"actix-files",
"actix-web",
"magick_rust",
"qstring",
]
[[package]]
@ -299,9 +300,9 @@ checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]]
name = "bindgen"
version = "0.66.1"
version = "0.68.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
dependencies = [
"bitflags 2.4.0",
"cexpr",
@ -785,9 +786,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "magick_rust"
version = "0.19.0"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce666df6ace809001c625ad383f4727657c7c7881df43b3b71486e8e9f07d017"
checksum = "c912b69250bcd5d024852a1a75c567d3b5d881871a55b741018741632a921bf8"
dependencies = [
"bindgen",
"libc",
@ -952,6 +953,15 @@ dependencies = [
"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]]
name = "quote"
version = "1.0.33"

View File

@ -8,4 +8,5 @@ edition = "2021"
[dependencies]
actix-web = "4"
actix-files = "0.6.2"
magick_rust = "0.19.0"
magick_rust = "0.19.1"
qstring = "0.7.2"

View File

@ -1,25 +1,34 @@
FROM rust:slim-bullseye AS build-env
FROM rust:latest
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
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 . .
RUN apt update && apt install wget -y && \
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 && \
RUN cargo build --release && \
cp ./target/release/ActixAPI . && \
mv small-image.png ./static && \
mv big-image.png ./static && \
mv video.mp4 ./static
mv video.mp4 ./static && \
ldconfig /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
ENV LD_LIBRARY_PATH=/usr/local/lib
ENTRYPOINT ["./ActixAPI"]

View File

@ -1,22 +1,13 @@
use qstring::QString;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, HttpRequest, Result};
use actix_files::NamedFile;
use magick_rust::{MagickWand, ToMagick};
use magick_rust::{self, FilterType};
use magick_rust::MagickWand;
#[get("/status/ok")]
async fn hello() -> impl Responder {
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> {
let path: &str = req.path();
let real_path = &path[1..];
@ -24,19 +15,50 @@ async fn static_serve(req: HttpRequest) -> Result<NamedFile> {
Ok(NamedFile::open(real_path)?)
}
#[post("/image/blur")]
async fn blur_image(image_data: web::Bytes) -> Result<HttpResponse> {
// Load the image from the incoming bytes
let image = ToMagick::<magick_rust::MagickWand>::to_magick(image_data).unwrap();
#[get("/image/load-small-image")]
async fn load_small_image() -> Result<NamedFile> {
let real_path = "static/small-image.png";
// Apply a blur filter to the image
let blurred_image = image.blur(FilterType::Gaussian, 0.0).unwrap();
Ok(NamedFile::open(real_path)?)
}
// Convert the blurred image back to bytes
let blurred_image_bytes = blurred_image.to_bytes().unwrap();
#[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("image/jpeg")
.content_type("application/json")
.body("{\"status\": 200}"))
}
#[post("/image/blur")]
async fn blur_image(image_data: web::Bytes, req: HttpRequest) -> Result<HttpResponse> {
// Load the image from the incoming bytes
let mut wand = MagickWand::new();
wand.read_image_blob(&image_data).unwrap();
let query_str = req.query_string();
let qs = QString::from(query_str);
let radius = qs.get("radius").unwrap_or("5").parse::<f64>().unwrap_or(5.0);
// Blur the image
wand.blur_image(radius, radius).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()
.content_type("image/png")
.body(blurred_image_bytes))
}
@ -48,11 +70,13 @@ async fn main() -> std::io::Result<()> {
App::new()
.route("/static/{filename:.*}", web::get().to(static_serve))
.service(hello)
.service(echo)
.service(load_small_image)
.service(load_big_image)
.service(save_big_image)
.service(blur_image)
.route("/hey", web::get().to(manual_hello))
.app_data(web::PayloadConfig::new(1024 * 1024 * 1024))
})
.bind(("0.0.0.0", 9090))?
.bind(("0.0.0.0", 5000))?
.run()
.await
}

View File

@ -31,7 +31,7 @@ services:
build: ./ActixAPI
restart: always
ports:
- "9083:9090"
- "9083:5000"
deploy:
resources:
limits:

View File

@ -13,6 +13,8 @@ ENDPOINTS = {
}
BLUR_RADIUS = 5
AVG_RUNS = 3
API_REQUESTS = [
('/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()),

View File

@ -1,6 +1,5 @@
import numpy as np
import matplotlib.pyplot as plt
import os
from common import API_REQUESTS, FRAMEWORKS
FRAMEWORKS = [f for f, _ in FRAMEWORKS]

View File

@ -2,11 +2,10 @@ import requests
import docker
import concurrent.futures
import time
import sys
import os
from math import floor
from init import init
from common import FRAMEWORKS, ENDPOINTS, API_REQUESTS
from common import FRAMEWORKS, ENDPOINTS, API_REQUESTS, AVG_RUNS
init()
@ -62,43 +61,57 @@ def run_tests(endpoint, method, num_requests, metadata):
for num_request in num_requests:
if num_request <= 0: continue
ok_responses = 0
bad_responses = 0
server_errors = 0
cpu, ram = 0, 0
total_cpu, total_ram = 0, 0
total_time = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor:
url = f'{URL_BASE}{endpoint}'
for run in range(AVG_RUNS):
ok_responses = 0
bad_responses = 0
server_errors = 0
start_time = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor:
url = f'{URL_BASE}{endpoint}'
futures = []
#with requests.Session() as session:
# futures = [executor.submit(send_request, session, url) for _ in range(num_request)]
start_time = time.time()
half = floor(num_request/2)
for i in range(num_request):
futures.append(executor.submit(send_request, url, method, metadata))
futures = []
#with requests.Session() as session:
# futures = [executor.submit(send_request, session, url) for _ in range(num_request)]
if i == half:
cpu, ram = get_resource_usage()
half = floor(num_request/2)
for i in range(num_request):
futures.append(executor.submit(send_request, url, method, metadata))
concurrent.futures.wait(futures)
if i == half:
cpu, ram = get_resource_usage()
total_cpu += float(cpu)
total_ram += float(ram)
elapsed_time = time.time() - start_time
concurrent.futures.wait(futures)
for future in futures:
responses = future.result()
ok_responses += responses[2]
bad_responses += responses[4]
server_errors += responses[5]
elapsed_time = time.time() - start_time
total_time += elapsed_time
for future in futures:
responses = future.result()
ok_responses += responses[2]
bad_responses += responses[4]
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}]]")
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
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}]]")
record(files[0], num_request, f"{num_request/elapsed_time:.2f}")
record_resource(files[1], num_request, cpu, ram)
time.sleep(3)
def get_resource_usage():
if CONTAINER_NAME == "": return 0, 0

1
tcc-express Submodule

@ -0,0 +1 @@
Subproject commit 504b59278f6024219814649f2715b70561251c65