Node 伺服器
要產生獨立的 Node 伺服器,請使用 adapter-node
。
用法
使用 npm i -D @sveltejs/adapter-node
安裝,然後將轉接器新增至你的 svelte.config.js
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter()
}
};
部署
首先,使用 npm run build
建置你的應用程式。這會在轉接器選項中指定的輸出目錄中建立生產伺服器,預設為 build
。
你需要輸出目錄、專案的 package.json
和 node_modules
中的生產相依性才能執行應用程式。生產相依性可以透過複製 package.json
和 package-lock.json
,然後執行 npm ci --omit dev
來產生(如果你的應用程式沒有任何相依性,則可以跳過此步驟)。然後你可以使用此命令啟動你的應用程式
node build
開發相依性將使用 Rollup 綑綁到你的應用程式中。要控制是否綑綁或外部化給定的套件,請將其分別放入你的 package.json
中的 devDependencies
或 dependencies
中。
壓縮回應
你通常會想要壓縮來自伺服器的回應。如果你已經在反向代理伺服器之後部署你的伺服器以進行 SSL 或負載平衡,通常在該層處理壓縮會產生更好的效能,因為 Node.js 是單執行緒的。
但是,如果你要建置自訂伺服器並想要在那裡新增壓縮中介軟體,請注意,我們建議使用 @polka/compression
,因為 SvelteKit 會串流回應,而更流行的 compression
套件不支援串流,並且在使用時可能會導致錯誤。
環境變數
在 dev
和 preview
中,SvelteKit 會從你的 .env
檔案(或 .env.local
,或 .env.[mode]
,由 Vite 決定)讀取環境變數。
在生產中,.env
檔案不會自動載入。若要執行此操作,請在你的專案中安裝 dotenv
...
npm install dotenv
...並在執行建置的應用程式之前呼叫它
node -r dotenv/config build
如果你使用 Node.js v20.6+,則可以使用 --env-file
標記
node --env-file=.env build
PORT、HOST 和 SOCKET_PATH
預設情況下,伺服器將使用連接埠 3000 接受 0.0.0.0
上的連線。可以使用 PORT
和 HOST
環境變數自訂這些連線
HOST=127.0.0.1 PORT=4000 node build
或者,可以將伺服器設定為接受指定 Socket 路徑上的連線。當使用 SOCKET_PATH
環境變數完成此操作時,將會忽略 HOST
和 PORT
環境變數。
SOCKET_PATH=/tmp/socket node build
ORIGIN、PROTOCOL_HEADER、HOST_HEADER 和 PORT_HEADER
HTTP 沒有給 SvelteKit 一個可靠的方式來知道目前要求的 URL。告訴 SvelteKit 應用程式在哪裡提供服務的最簡單方法是設定 ORIGIN
環境變數
ORIGIN=https://my.site node build
# or e.g. for local previewing and testing
ORIGIN=https://127.0.0.1:3000 node build
有了這個,對 /stuff
路徑的要求將正確解析為 https://my.site/stuff
。或者,你可以指定標頭,這些標頭會告訴 SvelteKit 請求通訊協定和主機,SvelteKit 可以從中建構起始 URL
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build
x-forwarded-proto
和x-forwarded-host
是事實上的標準標頭,如果你使用反向代理伺服器(想想負載平衡器和 CDN),則會轉發原始通訊協定和主機。只有當你的伺服器位於受信任的反向代理伺服器之後時,才應設定這些變數;否則,用戶端可能會偽造這些標頭。如果你的代理伺服器託管在非標準連接埠上,並且你的反向代理伺服器支援
x-forwarded-port
,你也可以設定PORT_HEADER=x-forwarded-port
。
如果 adapter-node
無法正確判斷你的部署的 URL,則在使用 表單操作 時可能會遇到此錯誤
禁止跨網站 POST 表單提交
ADDRESS_HEADER 和 XFF_DEPTH
傳遞給 Hook 和端點的 RequestEvent 物件包含一個 event.getClientAddress()
函式,該函式會傳回用戶端的 IP 位址。預設情況下,這是連線的 remoteAddress
。如果你的伺服器位於一個或多個代理伺服器(例如負載平衡器)之後,則此值將包含最內層代理伺服器的 IP 位址,而不是用戶端的 IP 位址,因此我們需要指定一個 ADDRESS_HEADER
來從中讀取位址
ADDRESS_HEADER=True-Client-IP node build
標頭很容易被偽造。與
PROTOCOL_HEADER
和HOST_HEADER
一樣,在設定這些標頭之前,你應該知道你在做什麼。
如果 ADDRESS_HEADER
是 X-Forwarded-For
,則標頭值將包含以逗號分隔的 IP 位址清單。XFF_DEPTH
環境變數應指定有多少個受信任的代理伺服器位於你的伺服器前面。例如,如果有三個受信任的代理伺服器,代理伺服器 3 會轉發原始連線和前兩個代理伺服器的位址
<client address>, <proxy 1 address>, <proxy 2 address>
某些指南會告訴你讀取最左邊的位址,但是這會讓你容易受到偽造
<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>
我們改為從右邊讀取,並考慮受信任的代理伺服器數量。在這種情況下,我們將使用 XFF_DEPTH=3
。
如果你需要改為讀取最左邊的位址(並且不在乎偽造)— 例如,為了提供地理位置服務,其中 IP 位址真實比受信任更重要,你可以透過檢查應用程式內的
x-forwarded-for
標頭來實現。
BODY_SIZE_LIMIT
要接受的最大請求主體大小(以位元組為單位),包括在串流期間。主體大小也可以使用千位元組 (K
)、兆位元組 (M
) 或吉位元組 (G
) 的單位後綴來指定。例如,512K
或 1M
。預設值為 512kb。你可以使用值 Infinity
(在轉接器的較舊版本中為 0)停用此選項,並在 handle
中實作自訂檢查,如果你需要更進階的功能。
SHUTDOWN_TIMEOUT
在收到 SIGTERM
或 SIGINT
訊號後,強制關閉任何剩餘連線之前要等待的秒數。預設值為 30
。在內部,轉接器會呼叫 closeAllConnections
。請參閱優雅關閉以取得更多詳細資訊。
IDLE_TIMEOUT
當使用 systemd Socket 啟動時,IDLE_TIMEOUT
指定在未收到任何請求時,應用程式自動進入休眠狀態的秒數。如果未設定,則應用程式會持續執行。請參閱Socket 啟動以取得更多詳細資訊。
選項
可以使用各種選項設定轉接器
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter({
// default options are shown
out: string
out: 'build',
precompress: boolean
precompress: true,
envPrefix: string
envPrefix: ''
})
}
};
out
要將伺服器建置到的目錄。它預設為 build
— 即 node build
會在建立後在本地啟動伺服器。
precompress
啟用使用 gzip 和 brotli 預先壓縮資產和預先呈現的頁面。預設值為 true
。
envPrefix
如果你需要變更用於設定部署的環境變數名稱(例如,為了消除與你不控制的環境變數的衝突),你可以指定前綴
envPrefix: 'MY_CUSTOM_';
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build
優雅關閉
預設情況下,當收到 SIGTERM
或 SIGINT
訊號時,adapter-node
會優雅地關閉 HTTP 伺服器。它會
- 拒絕新的請求 (
server.close
) - 等待已經發出但尚未收到回應的請求完成,並在連線閒置後關閉連線 (
server.closeIdleConnections
) - 最後,在經過
SHUTDOWN_TIMEOUT
秒後,關閉所有仍然處於活動狀態的連線。(server.closeAllConnections
)
如果您想要自訂此行為,可以使用自訂伺服器。
您可以監聽 sveltekit:shutdown
事件,該事件會在 HTTP 伺服器關閉所有連線後發出。與 Node 的 exit
事件不同,sveltekit:shutdown
事件支援非同步操作,並且總是在所有連線關閉時發出,即使伺服器有未完成的工作,例如開啟的資料庫連線。
var process: NodeJS.Process
process.NodeJS.Process.on(event: string | symbol, listener: (...args: any[]) => void): NodeJS.Process (+12 overloads)
Adds the listener
function to the end of the listeners array for the event
named eventName
. No checks are made to see if the listener
has already
been added. Multiple calls passing the same combination of eventName
and
listener
will result in the listener
being added, and called, multiple times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter
, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The emitter.prependListener()
method can be used as an alternative to add the
event listener to the beginning of the listeners array.
import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
on('sveltekit:shutdown', async (reason: any
reason) => {
await jobs.stop();
await db.close();
});
參數 reason
具有以下其中一個值:
SIGINT
- 關閉是由SIGINT
訊號觸發的SIGTERM
- 關閉是由SIGTERM
訊號觸發的IDLE
- 關閉是由IDLE_TIMEOUT
觸發的
Socket 啟動
現今大多數 Linux 作業系統都使用名為 systemd 的現代程序管理器來啟動伺服器並執行和管理服務。您可以配置您的伺服器以分配一個 socket 並根據需求啟動和擴展您的應用程式。這稱為socket 啟動。在這種情況下,作業系統會將兩個環境變數傳遞給您的應用程式 — LISTEN_PID
和 LISTEN_FDS
。然後,adapter 會監聽檔案描述符 3,它指向您必須建立的 systemd socket 單元。
您仍然可以將
envPrefix
用於 systemd socket 啟動。LISTEN_PID
和LISTEN_FDS
始終在沒有前綴的情況下讀取。
要利用 socket 啟動,請按照以下步驟操作。
- 將您的應用程式作為systemd 服務執行。它可以直接在主機系統上或在容器內(例如使用 Docker 或 systemd 可攜式服務)執行。如果您額外將
IDLE_TIMEOUT
環境變數傳遞給您的應用程式,如果IDLE_TIMEOUT
秒內沒有請求,它將會優雅地關閉。當有新的請求進來時,systemd 會自動再次啟動您的應用程式。
[Service]
Environment=NODE_ENV=production IDLE_TIMEOUT=60
ExecStart=/usr/bin/node /usr/bin/myapp/build
- 建立一個配套的socket 單元。adapter 只接受單個 socket。
[Socket]
ListenStream=3000
[Install]
WantedBy=sockets.target
- 執行
sudo systemctl daemon-reload
以確保 systemd 已識別到這兩個單元。然後使用sudo systemctl enable --now myapp.socket
在開機時啟用 socket 並立即啟動它。一旦對localhost:3000
發出第一個請求,應用程式就會自動啟動。
自訂伺服器
adapter 會在您的建置目錄中建立兩個檔案 — index.js
和 handler.js
。如果您使用預設的建置目錄,執行 index.js
— 例如 node build
— 將會在配置的端口上啟動伺服器。
或者,您可以匯入 handler.js
檔案,該檔案會匯出一個適用於 Express、Connect 或 Polka(甚至只是內建的 http.createServer
)的處理程式,並設定您自己的伺服器。
import { import handler
handler } from './build/handler.js';
import import express
express from 'express';
const const app: any
app = import express
express();
// add a route that lives separately from the SvelteKit app
const app: any
app.get('/healthcheck', (req, res) => {
res: any
res.end('ok');
});
// let SvelteKit handle everything else, including serving prerendered pages and static assets
const app: any
app.use(import handler
handler);
const app: any
app.listen(3000, () => {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log('listening on port 3000');
});