Penyelesaian awan bagus untuk projek sederhana dan besar, tetapi terlalu berat untuk projek peribadi kecil. Jika anda ingin melancarkan sesuatu yang kecil (beberapa titik akhir api dan repositori kecil), terdapat tiga pilihan:
Saya biasanya menulis aplikasi kecil saya menggunakan SQLite, ia adalah pangkalan data fail tunggal kecil yang berguna yang berfungsi dalam mana-mana bahasa pengaturcaraan dan boleh disalin ke mesin tempatan untuk menganalisis data contohnya. Jadi saya sedang mencari beberapa penyelesaian middleware yang menggabungkan pendekatan tanpa khidmat, kemudahan penggunaan dan keupayaan untuk menggunakan SQLite dan menemui Fly.io.
Jika anda tidak mempunyai akaun dalam Fly.io – anda perlu menciptanya. Juga alat CLI yang dipanggil flyctl diperlukan untuk menguruskan projek. Fly.io boleh digunakan secara tempatan dan dari CI.
flyctl membuat penggunaan daripada folder akar projek daripada Dockerfile, yang bagus, kerana Dockerfile yang sama boleh digunakan dalam sistem lain. Untuk bermain dengan Fly.io, saya menyediakan projek FastAPI ringkas yang menyimpan keadaan dalam pangkalan data – pemendek url generik dengan pengiraan klik.
Fail Docker:
FROM python:3.13-alpine WORKDIR /app COPY ./requirements.txt . RUN pip install --no-cache-dir --upgrade -r requirements.txt COPY . /app ENV HOST=0.0.0.0 PORT=8080 EXPOSE ${PORT} CMD uvicorn main:app --host ${HOST} --port ${PORT}
main.py:
import asyncio import random import string from urllib.parse import urlparse import aiosqlite from fastapi import FastAPI, HTTPException, Request from fastapi.responses import RedirectResponse DB_PATH = "/data/app.db" app = FastAPI() async def get_db() -> aiosqlite.Connection: if db := getattr(get_db, "_db", None): if db.is_alive: return db db = await aiosqlite.connect(DB_PATH, loop=asyncio.get_event_loop()) db.row_factory = aiosqlite.Row qs = """ CREATE TABLE IF NOT EXISTS links ( created_at INTEGER DEFAULT (strftime('%s', 'now')), short_code TEXT PRIMARY KEY, full_url TEXT NOT NULL, clicks INTEGER DEFAULT 0 ) """ await db.execute(qs) await db.commit() setattr(get_db, "_db", db) return db def random_code(length=8) -> str: alphabet = string.ascii_letters + string.digits return "".join(random.choice(alphabet) for x in range(length)) def is_valid_url(url: str) -> bool: try: parts = urlparse(url) return all([parts.scheme, parts.netloc]) except ValueError: return False @app.post("/") async def shorten(url: str, req: Request): if not is_valid_url(url): raise HTTPException(status_code=400, detail="Invalid URL") host = req.headers.get("host") if host is None: raise HTTPException(status_code=500, detail="Missing host header") short_code = random_code() db = await get_db() qs = "INSERT INTO links (short_code, full_url) VALUES (?, ?)" await db.execute(qs, (short_code, url)) await db.commit() return f"https://{host}/{short_code}" @app.get("/") async def list_links(): db = await get_db() qs = "SELECT short_code, full_url, clicks FROM links ORDER BY created_at DESC" async with db.execute(qs) as cursor: return await cursor.fetchall() @app.get("/{short_code}") async def redirect(short_code: str): db = await get_db() qs = """ UPDATE links SET clicks = clicks + 1 WHERE short_code = ? RETURNING full_url """ async with db.execute(qs, (short_code,)) as cursor: if row := await cursor.fetchone(): return RedirectResponse(row["full_url"]) raise HTTPException(status_code=404)
keperluan.txt:
aiosqlite fastapi uvicorn
Untuk menggunakan kod kami, mula-mula kami perlu mencipta projek Fly.io. Ini boleh dilakukan sama ada dalam antara muka web atau dengan flyctl. Untuk membuat proejct dengan alat CLU dalam folder akar (di mana kod terletak) pelancaran flyctl harus dijalankan. Perintah ini akan menawarkan untuk memilih perkakasan yang diingini dan akan mencipta fail fly.toml:
fly launch --build-only
Anda boleh mengubah suai projek pada masa hadapan dengan menukar parameter dalam fail ini atau melalui ui web. Fly.toml asas kelihatan baik, tetapi SQLite memerlukan Storan, yang boleh dibuat dengan:
fly volumes create sqlite_data -s 1 -r ams
di mana -s 1 menetapkan saiz volum kepada 1 GB (lalai ialah 3 GB), dan -r ialah rantau di mana volum akan dibuat (gunakan kawasan yang sama di mana projek Fly.io dibuat). Anda sentiasa boleh menukar saiz storan kemudian.
Perkara terakhir yang perlu dilakukan ialah menambah bahagian pelekap pada fly.toml, yang melampirkan kelantangan pada aplikasi:
FROM python:3.13-alpine WORKDIR /app COPY ./requirements.txt . RUN pip install --no-cache-dir --upgrade -r requirements.txt COPY . /app ENV HOST=0.0.0.0 PORT=8080 EXPOSE ${PORT} CMD uvicorn main:app --host ${HOST} --port ${PORT}
sqlite_data ialah nama storan, /data ialah laluan di mana volum akan disambungkan. Ini pada asasnya sama seperti docker run --mount source=sqlite_data,target=/data atau bahagian Docker Compose yang sepadan.
SQLite tidak boleh ditulis daripada lebih daripada satu apl dan Fly.io secara lalai mencipta 2 tika untuk apl, jadi kami boleh menentukan bilangan replika sebagai satu untuk berjaga-jaga:
import asyncio import random import string from urllib.parse import urlparse import aiosqlite from fastapi import FastAPI, HTTPException, Request from fastapi.responses import RedirectResponse DB_PATH = "/data/app.db" app = FastAPI() async def get_db() -> aiosqlite.Connection: if db := getattr(get_db, "_db", None): if db.is_alive: return db db = await aiosqlite.connect(DB_PATH, loop=asyncio.get_event_loop()) db.row_factory = aiosqlite.Row qs = """ CREATE TABLE IF NOT EXISTS links ( created_at INTEGER DEFAULT (strftime('%s', 'now')), short_code TEXT PRIMARY KEY, full_url TEXT NOT NULL, clicks INTEGER DEFAULT 0 ) """ await db.execute(qs) await db.commit() setattr(get_db, "_db", db) return db def random_code(length=8) -> str: alphabet = string.ascii_letters + string.digits return "".join(random.choice(alphabet) for x in range(length)) def is_valid_url(url: str) -> bool: try: parts = urlparse(url) return all([parts.scheme, parts.netloc]) except ValueError: return False @app.post("/") async def shorten(url: str, req: Request): if not is_valid_url(url): raise HTTPException(status_code=400, detail="Invalid URL") host = req.headers.get("host") if host is None: raise HTTPException(status_code=500, detail="Missing host header") short_code = random_code() db = await get_db() qs = "INSERT INTO links (short_code, full_url) VALUES (?, ?)" await db.execute(qs, (short_code, url)) await db.commit() return f"https://{host}/{short_code}" @app.get("/") async def list_links(): db = await get_db() qs = "SELECT short_code, full_url, clicks FROM links ORDER BY created_at DESC" async with db.execute(qs) as cursor: return await cursor.fetchall() @app.get("/{short_code}") async def redirect(short_code: str): db = await get_db() qs = """ UPDATE links SET clicks = clicks + 1 WHERE short_code = ? RETURNING full_url """ async with db.execute(qs, (short_code,)) as cursor: if row := await cursor.fetchone(): return RedirectResponse(row["full_url"]) raise HTTPException(status_code=404)
Semua konfigurasi telah dilakukan sekarang dan kami boleh menggunakan apl kami dengan arahan:
aiosqlite fastapi uvicorn
Apl harus berjaya but dan nama DNS awam akan dicetak ke konsol. Kini kita boleh menyemaknya dengan menyiarkan beberapa url kepada pemendek:
fly launch --build-only
Kemudian kita boleh melawati pautan ini, ia harus mengubah hala ke https://example.com. Akhir sekali, kami boleh menyemak bahawa klik telah dikemas kini:
fly volumes create sqlite_data -s 1 -r ams
Untuk menyemak keadaan pangkalan data yang disimpan antara kerahan, kami boleh melakukan kerahan baharu dengan penggunaan terbang dan menyemak senarai pautan kekal sama seperti di atas (1 pautan, 1 klik).
Jika anda menggunakan penyelesaian luaran untuk migrasi, dan bukannya menjalankannya daripada kod apabila apl dimulakan, maka satu-satunya cara untuk menjalankan migrasi ialah meletakkannya dalam Dockerfile sebagai sebahagian daripada arahan RUN.
Kami boleh menyambung ke mesin dengan konsol fly ssh dan kemudian dalam folder /data berinteraksi dengan fail pangkalan data. Juga kita boleh menyalin fail pangkalan data ke mesin tempatan dengan:
[mounts] source = "sqlite_data" destination = "/data"
Fly.io ialah perkhidmatan yang ringkas dan mudah untuk menggunakan aplikasi. Sebarkan kerja dari Docker Containers, perkhidmatan tambahan termasuk PSQL, Redis, storan seperti S3 (tidak seperti Vercel). Ia murah, perkhidmatan termurah berharga 3 dolar (1 CPU kongsi / 256 MB) – mungkin lebih rendah, jika anda mempunyai sedikit trafik – kontena ditutup selepas beberapa minit tanpa aktiviti dan dihidupkan secara automatik apabila trafik muncul.
Pada sisi negatifnya, tiada penyelesaian terbina dalam untuk tugas yang dijadualkan – sebaliknya, penyelesaian rasmi adalah untuk menyediakan pelayan berasingan dengan crontab dan menjalankan tugas daripadanya – ia agak menyeramkan.
Atas ialah kandungan terperinci Gunakan aplikasi FastAPI dengan SQLite pada Fly.io. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!