Use single server for both ws and http (#230)
* Use single server for both ws and http * Format code
This commit is contained in:
parent
da1f19bf0b
commit
6a46dfcb6e
|
@ -13,9 +13,6 @@ spec:
|
||||||
- port: 4200
|
- port: 4200
|
||||||
name: rest-api
|
name: rest-api
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
- port: 6200
|
|
||||||
name: wss-api
|
|
||||||
protocol: TCP
|
|
||||||
clusterIP: None
|
clusterIP: None
|
||||||
selector:
|
selector:
|
||||||
app: pyth-price-service
|
app: pyth-price-service
|
||||||
|
@ -71,8 +68,6 @@ spec:
|
||||||
value: '[{"chain_id":1,"emitter_address":"71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b"}]'
|
value: '[{"chain_id":1,"emitter_address":"71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b"}]'
|
||||||
- name: REST_PORT
|
- name: REST_PORT
|
||||||
value: '4200'
|
value: '4200'
|
||||||
- name: WS_PORT
|
|
||||||
value: '6200'
|
|
||||||
- name: PROM_PORT
|
- name: PROM_PORT
|
||||||
value: '8081'
|
value: '8081'
|
||||||
- name: READINESS_SPY_SYNC_TIME_SECONDS
|
- name: READINESS_SPY_SYNC_TIME_SECONDS
|
||||||
|
|
|
@ -52,7 +52,7 @@ async function waitForMessages(messages: any[], cnt: number): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSocketClient(): Promise<[WebSocket, any[]]> {
|
async function createSocketClient(): Promise<[WebSocket, any[]]> {
|
||||||
const client = new WebSocket(`ws://localhost:${port}`);
|
const client = new WebSocket(`ws://localhost:${port}/ws`);
|
||||||
|
|
||||||
await waitForSocketState(client, client.OPEN);
|
await waitForSocketState(client, client.OPEN);
|
||||||
|
|
||||||
|
@ -79,9 +79,12 @@ beforeAll(async () => {
|
||||||
getPriceIds: () => new Set(priceFeeds.map((priceFeed) => priceFeed.id)),
|
getPriceIds: () => new Set(priceFeeds.map((priceFeed) => priceFeed.id)),
|
||||||
};
|
};
|
||||||
|
|
||||||
api = new WebSocketAPI({ port }, priceInfo);
|
api = new WebSocketAPI(priceInfo);
|
||||||
|
|
||||||
[wss, server] = api.run();
|
server = new Server();
|
||||||
|
server.listen(port);
|
||||||
|
|
||||||
|
wss = api.run(server);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
|
|
@ -20,43 +20,43 @@ setDefaultWasm("node");
|
||||||
// Set up the logger.
|
// Set up the logger.
|
||||||
initLogger({ logLevel: process.env.LOG_LEVEL });
|
initLogger({ logLevel: process.env.LOG_LEVEL });
|
||||||
|
|
||||||
const promClient = new PromClient({
|
async function run() {
|
||||||
name: "price_service",
|
const promClient = new PromClient({
|
||||||
port: parseInt(envOrErr("PROM_PORT")),
|
name: "price_service",
|
||||||
});
|
port: parseInt(envOrErr("PROM_PORT")),
|
||||||
|
});
|
||||||
|
|
||||||
const listener = new Listener(
|
const listener = new Listener(
|
||||||
{
|
{
|
||||||
spyServiceHost: envOrErr("SPY_SERVICE_HOST"),
|
spyServiceHost: envOrErr("SPY_SERVICE_HOST"),
|
||||||
filtersRaw: process.env.SPY_SERVICE_FILTERS,
|
filtersRaw: process.env.SPY_SERVICE_FILTERS,
|
||||||
readiness: {
|
readiness: {
|
||||||
spySyncTimeSeconds: parseInt(envOrErr("READINESS_SPY_SYNC_TIME_SECONDS")),
|
spySyncTimeSeconds: parseInt(
|
||||||
numLoadedSymbols: parseInt(envOrErr("READINESS_NUM_LOADED_SYMBOLS")),
|
envOrErr("READINESS_SPY_SYNC_TIME_SECONDS")
|
||||||
|
),
|
||||||
|
numLoadedSymbols: parseInt(envOrErr("READINESS_NUM_LOADED_SYMBOLS")),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
promClient
|
||||||
promClient
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// In future if we have more components we will modify it to include them all
|
// In future if we have more components we will modify it to include them all
|
||||||
const isReady = () => listener.isReady();
|
const isReady = () => listener.isReady();
|
||||||
|
|
||||||
const restAPI = new RestAPI(
|
const restAPI = new RestAPI(
|
||||||
{
|
{
|
||||||
port: parseInt(envOrErr("REST_PORT")),
|
port: parseInt(envOrErr("REST_PORT")),
|
||||||
},
|
},
|
||||||
listener,
|
listener,
|
||||||
isReady,
|
isReady,
|
||||||
promClient
|
promClient
|
||||||
);
|
);
|
||||||
|
|
||||||
const wsAPI = new WebSocketAPI(
|
const wsAPI = new WebSocketAPI(listener, promClient);
|
||||||
{
|
|
||||||
port: parseInt(envOrErr("WS_PORT")),
|
|
||||||
},
|
|
||||||
listener,
|
|
||||||
promClient
|
|
||||||
);
|
|
||||||
|
|
||||||
listener.run();
|
listener.run();
|
||||||
restAPI.run();
|
const server = await restAPI.run();
|
||||||
wsAPI.run();
|
wsAPI.run(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
|
@ -168,8 +168,12 @@ export class Listener implements PriceStore {
|
||||||
let isAnyPriceNew = batchAttestation.priceAttestations.some(
|
let isAnyPriceNew = batchAttestation.priceAttestations.some(
|
||||||
(priceAttestation) => {
|
(priceAttestation) => {
|
||||||
const key = priceAttestation.priceId;
|
const key = priceAttestation.priceId;
|
||||||
let lastAttestationTime = this.priceFeedVaaMap.get(key)?.attestationTime;
|
let lastAttestationTime =
|
||||||
return lastAttestationTime === undefined || lastAttestationTime < priceAttestation.attestationTime;
|
this.priceFeedVaaMap.get(key)?.attestationTime;
|
||||||
|
return (
|
||||||
|
lastAttestationTime === undefined ||
|
||||||
|
lastAttestationTime < priceAttestation.attestationTime
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -182,7 +186,10 @@ export class Listener implements PriceStore {
|
||||||
|
|
||||||
let lastAttestationTime = this.priceFeedVaaMap.get(key)?.attestationTime;
|
let lastAttestationTime = this.priceFeedVaaMap.get(key)?.attestationTime;
|
||||||
|
|
||||||
if (lastAttestationTime === undefined || lastAttestationTime < priceAttestation.attestationTime) {
|
if (
|
||||||
|
lastAttestationTime === undefined ||
|
||||||
|
lastAttestationTime < priceAttestation.attestationTime
|
||||||
|
) {
|
||||||
const priceFeed = priceAttestationToPriceFeed(priceAttestation);
|
const priceFeed = priceAttestationToPriceFeed(priceAttestation);
|
||||||
this.priceFeedVaaMap.set(key, {
|
this.priceFeedVaaMap.set(key, {
|
||||||
seqNum: parsedVAA.sequence,
|
seqNum: parsedVAA.sequence,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { PromClient } from "./promClient";
|
||||||
import { DurationInMs, DurationInSec } from "./helpers";
|
import { DurationInMs, DurationInSec } from "./helpers";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { validate, ValidationError, Joi, schema } from "express-validation";
|
import { validate, ValidationError, Joi, schema } from "express-validation";
|
||||||
|
import { Server } from "http";
|
||||||
|
|
||||||
const MORGAN_LOG_FORMAT =
|
const MORGAN_LOG_FORMAT =
|
||||||
':remote-addr - :remote-user ":method :url HTTP/:http-version"' +
|
':remote-addr - :remote-user ":method :url HTTP/:http-version"' +
|
||||||
|
@ -80,7 +81,7 @@ export class RestAPI {
|
||||||
}).required(),
|
}).required(),
|
||||||
};
|
};
|
||||||
app.get(
|
app.get(
|
||||||
"/latest_vaas",
|
"/api/latest_vaas",
|
||||||
validate(latestVaasInputSchema),
|
validate(latestVaasInputSchema),
|
||||||
(req: Request, res: Response) => {
|
(req: Request, res: Response) => {
|
||||||
let priceIds = req.query.ids as string[];
|
let priceIds = req.query.ids as string[];
|
||||||
|
@ -126,7 +127,7 @@ export class RestAPI {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
endpoints.push(
|
endpoints.push(
|
||||||
"latest_vaas?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&.."
|
"api/latest_vaas?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&.."
|
||||||
);
|
);
|
||||||
|
|
||||||
const latestPriceFeedsInputSchema: schema = {
|
const latestPriceFeedsInputSchema: schema = {
|
||||||
|
@ -137,7 +138,7 @@ export class RestAPI {
|
||||||
}).required(),
|
}).required(),
|
||||||
};
|
};
|
||||||
app.get(
|
app.get(
|
||||||
"/latest_price_feeds",
|
"/api/latest_price_feeds",
|
||||||
validate(latestPriceFeedsInputSchema),
|
validate(latestPriceFeedsInputSchema),
|
||||||
(req: Request, res: Response) => {
|
(req: Request, res: Response) => {
|
||||||
let priceIds = req.query.ids as string[];
|
let priceIds = req.query.ids as string[];
|
||||||
|
@ -177,7 +178,7 @@ export class RestAPI {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
endpoints.push(
|
endpoints.push(
|
||||||
"latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&.."
|
"api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&.."
|
||||||
);
|
);
|
||||||
|
|
||||||
app.get("/ready", (_, res: Response) => {
|
app.get("/ready", (_, res: Response) => {
|
||||||
|
@ -194,6 +195,9 @@ export class RestAPI {
|
||||||
});
|
});
|
||||||
endpoints.push("live");
|
endpoints.push("live");
|
||||||
|
|
||||||
|
// Websocket endpoint
|
||||||
|
endpoints.push("ws");
|
||||||
|
|
||||||
app.get("/", (_, res: Response) => res.json(endpoints));
|
app.get("/", (_, res: Response) => res.json(endpoints));
|
||||||
|
|
||||||
app.use(function (err: any, _: Request, res: Response, next: NextFunction) {
|
app.use(function (err: any, _: Request, res: Response, next: NextFunction) {
|
||||||
|
@ -211,9 +215,9 @@ export class RestAPI {
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run(): Promise<Server> {
|
||||||
let app = await this.createApp();
|
let app = await this.createApp();
|
||||||
app.listen(this.port, () =>
|
return app.listen(this.port, () =>
|
||||||
logger.debug("listening on REST port " + this.port)
|
logger.debug("listening on REST port " + this.port)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,19 +34,13 @@ export type ServerMessage = ServerResponse | ServerPriceUpdate;
|
||||||
|
|
||||||
export class WebSocketAPI {
|
export class WebSocketAPI {
|
||||||
private wsCounter: number;
|
private wsCounter: number;
|
||||||
private port: number;
|
|
||||||
private priceFeedClients: Map<HexString, Set<WebSocket>>;
|
private priceFeedClients: Map<HexString, Set<WebSocket>>;
|
||||||
private aliveClients: Set<WebSocket>;
|
private aliveClients: Set<WebSocket>;
|
||||||
private wsId: Map<WebSocket, number>;
|
private wsId: Map<WebSocket, number>;
|
||||||
private priceFeedVaaInfo: PriceStore;
|
private priceFeedVaaInfo: PriceStore;
|
||||||
private promClient: PromClient | undefined;
|
private promClient: PromClient | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(priceFeedVaaInfo: PriceStore, promClient?: PromClient) {
|
||||||
config: { port: number },
|
|
||||||
priceFeedVaaInfo: PriceStore,
|
|
||||||
promClient?: PromClient
|
|
||||||
) {
|
|
||||||
this.port = config.port;
|
|
||||||
this.priceFeedVaaInfo = priceFeedVaaInfo;
|
this.priceFeedVaaInfo = priceFeedVaaInfo;
|
||||||
this.priceFeedClients = new Map();
|
this.priceFeedClients = new Map();
|
||||||
this.aliveClients = new Set();
|
this.aliveClients = new Set();
|
||||||
|
@ -167,11 +161,8 @@ export class WebSocketAPI {
|
||||||
ws.send(JSON.stringify(response));
|
ws.send(JSON.stringify(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
run(): [WebSocketServer, http.Server] {
|
run(server: http.Server): WebSocketServer {
|
||||||
const app = express();
|
const wss = new WebSocketServer({ server, path: "/ws" });
|
||||||
const server = http.createServer(app);
|
|
||||||
|
|
||||||
const wss = new WebSocketServer({ server });
|
|
||||||
|
|
||||||
wss.on("connection", (ws: WebSocket, request: http.IncomingMessage) => {
|
wss.on("connection", (ws: WebSocket, request: http.IncomingMessage) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@ -220,12 +211,10 @@ export class WebSocketAPI {
|
||||||
clearInterval(pingInterval);
|
clearInterval(pingInterval);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(this.port, () =>
|
|
||||||
logger.debug("listening on WS port " + this.port)
|
|
||||||
);
|
|
||||||
this.priceFeedVaaInfo.addUpdateListener(
|
this.priceFeedVaaInfo.addUpdateListener(
|
||||||
this.dispatchPriceFeedUpdate.bind(this)
|
this.dispatchPriceFeedUpdate.bind(this)
|
||||||
);
|
);
|
||||||
return [wss, server];
|
|
||||||
|
return wss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue