fix IPv6 connectivity

This commit is contained in:
whyour 2026-05-06 01:29:01 +08:00
parent 1315578878
commit 3464c4da61
4 changed files with 71 additions and 26 deletions

View File

@ -9,6 +9,8 @@ dotenv.config({
interface Config { interface Config {
port: number; port: number;
grpcPort: number; grpcPort: number;
bindHost: string;
bindHostGrpc: string;
nodeEnv: string; nodeEnv: string;
isDevelopment: boolean; isDevelopment: boolean;
isProduction: boolean; isProduction: boolean;
@ -31,6 +33,8 @@ interface Config {
const config: Config = { const config: Config = {
port: parseInt(process.env.BACK_PORT || '5700', 10), port: parseInt(process.env.BACK_PORT || '5700', 10),
grpcPort: parseInt(process.env.GRPC_PORT || '5500', 10), grpcPort: parseInt(process.env.GRPC_PORT || '5500', 10),
bindHost: process.env.BIND_HOST || '::',
bindHostGrpc: process.env.BIND_HOST_GRPC || '::',
nodeEnv: process.env.NODE_ENV || 'development', nodeEnv: process.env.NODE_ENV || 'development',
isDevelopment: process.env.NODE_ENV === 'development', isDevelopment: process.env.NODE_ENV === 'development',
isProduction: process.env.NODE_ENV === 'production', isProduction: process.env.NODE_ENV === 'production',

View File

@ -10,7 +10,7 @@ import config from '../config';
class Client { class Client {
private client = new CronClient( private client = new CronClient(
`0.0.0.0:${config.grpcPort}`, `localhost:${config.grpcPort}`,
credentials.createInsecure(), credentials.createInsecure(),
{ 'grpc.enable_http_proxy': 0 }, { 'grpc.enable_http_proxy': 0 },
); );

View File

@ -16,6 +16,13 @@ import { Service } from 'typedi';
export class GrpcServerService { export class GrpcServerService {
private server: Server = new Server({ 'grpc.enable_http_proxy': 0 }); private server: Server = new Server({ 'grpc.enable_http_proxy': 0 });
private formatGrpcAddress(host: string, port: number): string {
if (host === '::') {
return `[::]:${port}`;
}
return `${host}:${port}`;
}
async initialize() { async initialize() {
try { try {
this.server.addService(HealthService, { check }); this.server.addService(HealthService, { check });
@ -23,18 +30,32 @@ export class GrpcServerService {
this.server.addService(ApiService, Api); this.server.addService(ApiService, Api);
const grpcPort = config.grpcPort; const grpcPort = config.grpcPort;
const hostsToTry = [
config.bindHostGrpc,
...(config.bindHostGrpc !== '0.0.0.0' ? ['0.0.0.0'] : [])
];
const bindAsync = promisify(this.server.bindAsync).bind(this.server); const bindAsync = promisify(this.server.bindAsync).bind(this.server);
await bindAsync(
`0.0.0.0:${grpcPort}`,
ServerCredentials.createInsecure(),
);
Logger.debug(`✌️ gRPC service started successfully`);
metricsService.record('grpc_service_start', 1, { let lastError: Error | null = null;
port: grpcPort.toString(),
});
return grpcPort; for (const host of hostsToTry) {
try {
const address = this.formatGrpcAddress(host, grpcPort);
await bindAsync(address, ServerCredentials.createInsecure());
Logger.debug(`✌️ gRPC service started successfully on ${address}`);
metricsService.record('grpc_service_start', 1, {
port: grpcPort.toString(),
host
});
return grpcPort;
} catch (err) {
lastError = err as Error;
Logger.warn(`Failed to bind gRPC on ${host}:${grpcPort}, trying next...`, err);
}
}
Logger.error('Failed to start gRPC service on all hosts');
throw lastError || new Error('Failed to start gRPC service');
} catch (err) { } catch (err) {
Logger.error('Failed to start gRPC service:', err); Logger.error('Failed to start gRPC service:', err);
throw err; throw err;

View File

@ -3,31 +3,51 @@ import Logger from '../loaders/logger';
import { metricsService } from './metrics'; import { metricsService } from './metrics';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { Server } from 'http'; import { Server } from 'http';
import config from '../config';
@Service() @Service()
export class HttpServerService { export class HttpServerService {
private server?: Server = undefined; private server?: Server = undefined;
async initialize(expressApp: express.Application, port: number) { async initialize(expressApp: express.Application, port: number) {
try { const hostsToTry = [
return new Promise((resolve, reject) => { config.bindHost,
this.server = expressApp.listen(port, '0.0.0.0', () => { ...(config.bindHost !== '0.0.0.0' ? ['0.0.0.0'] : [])
Logger.debug(`✌️ HTTP service started successfully`); ];
metricsService.record('http_service_start', 1, {
port: port.toString(),
});
resolve(this.server);
});
this.server?.on('error', (err: Error) => { let lastError: Error | null = null;
Logger.error('Failed to start HTTP service:', err);
reject(err); for (const host of hostsToTry) {
try {
const server = await this.tryListen(expressApp, port, host);
Logger.debug(`✌️ HTTP service started successfully on ${host}:${port}`);
metricsService.record('http_service_start', 1, {
port: port.toString(),
host
}); });
}); this.server = server;
} catch (err) { return server;
Logger.error('Failed to start HTTP service:', err); } catch (err) {
throw err; lastError = err as Error;
Logger.warn(`Failed to bind HTTP on ${host}:${port}, trying next...`, err);
}
} }
Logger.error('Failed to start HTTP service on all hosts');
throw lastError || new Error('Failed to start HTTP service');
}
private async tryListen(expressApp: express.Application, port: number, host: string): Promise<Server> {
return new Promise((resolve, reject) => {
const server = expressApp.listen(port, host, () => {
resolve(server);
});
server.on('error', (err: Error) => {
server.close();
reject(err);
});
});
} }
async shutdown() { async shutdown() {