зеркало из https://github.com/Azure/Azurite.git
Refacted Server Implementation
This commit is contained in:
Родитель
3d0c24cc3a
Коммит
33c9525743
|
@ -1,7 +1,8 @@
|
|||
.nyc_output
|
||||
node_modules/
|
||||
coverage/
|
||||
dist
|
||||
typings/
|
||||
node_modules
|
||||
coverage
|
||||
azurite-testdrive
|
||||
__blobstorage__
|
||||
__blobpersistence__
|
||||
__azurite_db_blob__.json
|
||||
.nyc_output
|
||||
dist
|
||||
typings
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import * as http from "http";
|
||||
|
||||
import Server from "../common/IServer";
|
||||
import getAPP from "./app";
|
||||
import Configuration from "./Configuration";
|
||||
import { IDataStore } from "./persistence/IDataStore";
|
||||
import LokiBlobDataStore from "./persistence/LokiBlobDataStore";
|
||||
import { DEFAULT_BLOB_PERSISTENCE_PATH, DEFAULT_LOKI_DB_PATH } from "./utils/constants";
|
||||
import logger from "./utils/log/Logger";
|
||||
|
||||
// Decouple server & app layer
|
||||
// Server layer should only care about sever related configurations, like listening port etc.
|
||||
// App layer will handle middleware related things
|
||||
|
||||
/**
|
||||
* Azurite HTTP server.
|
||||
*
|
||||
* @export
|
||||
* @class Server
|
||||
*/
|
||||
export default class BlobServer extends Server {
|
||||
private readonly dataStore: IDataStore;
|
||||
|
||||
/**
|
||||
* Creates an instance of Server.
|
||||
*
|
||||
* @param {Configuration} configuration
|
||||
* @memberof Server
|
||||
*/
|
||||
constructor(configuration: Configuration) {
|
||||
const host = configuration.host;
|
||||
const port = configuration.port;
|
||||
const dataStore = new LokiBlobDataStore(
|
||||
configuration.dbPath || DEFAULT_LOKI_DB_PATH,
|
||||
configuration.persistencePath || DEFAULT_BLOB_PERSISTENCE_PATH
|
||||
);
|
||||
const httpServer = http.createServer(getAPP(dataStore));
|
||||
|
||||
super(host, port, httpServer);
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
protected async beforeStart(): Promise<void> {
|
||||
await this.dataStore.init();
|
||||
}
|
||||
|
||||
protected async afterStart(): Promise<void> {
|
||||
let address = this.httpServer.address();
|
||||
if (typeof address !== "string") {
|
||||
address = address.address;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Azurite Blob service successfully listens on ${address}:${this.port}`
|
||||
);
|
||||
}
|
||||
protected async beforeClose(): Promise<void> {
|
||||
logger.info(
|
||||
`Azurite Blob service is shutdown... Waiting for existing keep-alive connections timeout...`
|
||||
);
|
||||
}
|
||||
|
||||
protected async afterClose(): Promise<void> {
|
||||
await this.dataStore.close();
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
import * as http from "http";
|
||||
|
||||
import getAPP from "./app";
|
||||
import Configuration from "./Configuration";
|
||||
import { IDataStore } from "./persistence/IDataStore";
|
||||
import LokiBlobDataStore from "./persistence/LokiBlobDataStore";
|
||||
import { DEFAULT_BLOB_PERSISTENCE_PATH, DEFAULT_LOKI_DB_PATH } from "./utils/constants";
|
||||
import logger from "./utils/log/Logger";
|
||||
|
||||
// Decouple server & app layer
|
||||
// Server layer should only care about sever related configurations, like listening port etc.
|
||||
// App layer will handle middleware related things
|
||||
|
||||
/**
|
||||
* Azurite HTTP server.
|
||||
*
|
||||
* @export
|
||||
* @class Server
|
||||
*/
|
||||
export default class Server {
|
||||
public readonly port: number;
|
||||
public readonly host: string;
|
||||
private readonly dataStore: IDataStore;
|
||||
private readonly httpServer: http.Server;
|
||||
|
||||
/**
|
||||
* Creates an instance of Server.
|
||||
*
|
||||
* @param {Configuration} configuration
|
||||
* @memberof Server
|
||||
*/
|
||||
constructor(configuration: Configuration) {
|
||||
this.host = configuration.host;
|
||||
this.port = configuration.port;
|
||||
const dataStore = (this.dataStore = new LokiBlobDataStore(
|
||||
configuration.dbPath || DEFAULT_LOKI_DB_PATH,
|
||||
configuration.persistencePath || DEFAULT_BLOB_PERSISTENCE_PATH
|
||||
));
|
||||
this.httpServer = http.createServer(getAPP(dataStore));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resources server needed, such as database connections and other resources.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
public async init(): Promise<void> {
|
||||
await this.dataStore.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts HTTP server.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
public async start(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.httpServer
|
||||
.listen(this.port, this.host, () => {
|
||||
let address = this.httpServer.address();
|
||||
if (typeof address !== "string") {
|
||||
address = address.address;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Azurite Blob service successfully listens on ${address}:${
|
||||
this.port
|
||||
}`
|
||||
);
|
||||
resolve();
|
||||
})
|
||||
.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close HTTP server, database connections or other resources.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
public async close(): Promise<void> {
|
||||
logger.info(`Azurite Blob service is shutdown... Waiting for existing keep-alive connections timeout...`);
|
||||
|
||||
// Close HTTP server first to deny incoming connections
|
||||
// You will find this will not close server immediately because there maybe existing keep-alive connections
|
||||
// Calling httpServer.close will only stop accepting incoming requests
|
||||
// and wait for existing keep-alive connections timeout
|
||||
// Default keep-alive timeout is 5 seconds defined by httpServer.keepAliveTimeout
|
||||
// TODO: Add a middleware to reject incoming request over existing keep-alive connections
|
||||
// https://github.com/nodejs/node/issues/2642
|
||||
await new Promise((resolve) => {
|
||||
this.httpServer.close(resolve);
|
||||
});
|
||||
|
||||
await this.dataStore.close();
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import Server from "../common/IServer";
|
||||
import BlobServer from "./BlobServer";
|
||||
import Configuration from "./Configuration";
|
||||
import Server from "./Server";
|
||||
|
||||
async function main() {
|
||||
const config = new Configuration();
|
||||
const server = new Server(config);
|
||||
await server.init();
|
||||
const server: Server = new BlobServer(config);
|
||||
await server.start();
|
||||
|
||||
process.on("message", (msg) => {
|
||||
|
|
|
@ -2,9 +2,9 @@ export const DEFAULT_SERVER_HOST_NAME = "127.0.0.1";
|
|||
|
||||
export const DEFAULT_SERVER_LISTENING_PORT = 10000;
|
||||
|
||||
export const DEFAULT_LOKI_DB_PATH = "__blobstorage__";
|
||||
export const DEFAULT_LOKI_DB_PATH = "__azurite_db_blob__.json";
|
||||
|
||||
export const DEFAULT_BLOB_PERSISTENCE_PATH = "__blobpersistence__";
|
||||
export const DEFAULT_BLOB_PERSISTENCE_PATH = "__blobstorage__";
|
||||
|
||||
export const DEFAULT_CONTEXT_PATH = "azurite_blob_context";
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import * as http from "http";
|
||||
|
||||
/**
|
||||
* Abstract Server class for Azurite Servers.
|
||||
*
|
||||
* @export
|
||||
* @abstract
|
||||
* @class Server
|
||||
*/
|
||||
export default abstract class Server {
|
||||
/**
|
||||
* Creates an instance of Server.
|
||||
*
|
||||
* @param {string} host
|
||||
* @param {number} port
|
||||
* @param {http.Server} httpServer
|
||||
* @memberof Server
|
||||
*/
|
||||
public constructor(
|
||||
public readonly host: string,
|
||||
public readonly port: number,
|
||||
protected readonly httpServer: http.Server
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialize and start the server to service incoming HTTP requests.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
public async start(): Promise<void> {
|
||||
await this.beforeStart();
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.httpServer
|
||||
.listen(this.port, this.host, resolve)
|
||||
.on("error", reject);
|
||||
});
|
||||
|
||||
await this.afterStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose HTTP server and clean up other resources.
|
||||
*
|
||||
* We name this method as close instead of dispose, because in practices, usually we cannot re-open the resources
|
||||
* disposed, but can re-open the resources closed.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
public async close(): Promise<void> {
|
||||
await this.beforeClose();
|
||||
|
||||
// Close HTTP server first to deny incoming connections
|
||||
// You will find this will not close server immediately because there maybe existing keep-alive connections
|
||||
// Calling httpServer.close will only stop accepting incoming requests
|
||||
// and wait for existing keep-alive connections timeout
|
||||
// Default keep-alive timeout is 5 seconds defined by httpServer.keepAliveTimeout
|
||||
// TODO: Add a middleware to reject incoming request over existing keep-alive connections
|
||||
// https://github.com/nodejs/node/issues/2642
|
||||
await new Promise(resolve => {
|
||||
this.httpServer.close(resolve);
|
||||
});
|
||||
|
||||
await this.afterClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async task before server starts.
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
protected abstract async beforeStart(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Async task after server starts.
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
protected abstract async afterStart(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Async task before server closes.
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
protected abstract async beforeClose(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Async task after server closes.
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
* @memberof Server
|
||||
*/
|
||||
protected abstract async afterClose(): Promise<void>;
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
import { Aborter, AnonymousCredential, ServiceURL, StorageURL } from "@azure/storage-blob";
|
||||
import * as assert from "assert";
|
||||
import { rmdirSync, unlinkSync } from "fs";
|
||||
|
||||
import {
|
||||
Aborter,
|
||||
AnonymousCredential,
|
||||
ServiceURL,
|
||||
StorageURL,
|
||||
} from "@azure/storage-blob";
|
||||
|
||||
import Server from "../../../src/blob/BlobServer";
|
||||
import Configuration from "../../../src/blob/Configuration";
|
||||
import Server from "../../../src/blob/Server";
|
||||
|
||||
// TODO: Create a server factory as tests utils
|
||||
const host = "127.0.0.1";
|
||||
|
@ -32,7 +26,6 @@ let server: Server;
|
|||
describe("ServiceHandler", () => {
|
||||
before(async () => {
|
||||
server = new Server(config);
|
||||
await server.init();
|
||||
await server.start();
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче