Appearance
端口发现服务 API
概述
端口发现服务(PortDiscovery)是 Eagle2Ae AE 扩展的一个核心组件,负责从Eagle注册的临时文件中发现服务端口。该服务提供了自动端口发现、服务验证、连接测试等功能,确保AE扩展能够正确连接到Eagle插件。
核心特性
自动端口发现
- 从系统临时目录读取Eagle注册文件
- 自动解析服务端口信息
- 支持端口缓存机制
服务验证
- 验证服务信息的有效性
- 检查端口范围和时间戳
- 提供服务过期检测
连接测试
- 测试发现端口的连接性
- 支持超时控制
- 提供详细的连接状态信息
技术实现
核心类结构
javascript
/**
* 端口发现服务
* 从Eagle注册的临时文件中发现服务端口
*/
class PortDiscovery {
/**
* 构造函数
* @param {Function} logger - 日志函数
*/
constructor(logger) {
this.logger = logger || console.log;
this.registryFile = null;
this.cachedPort = null;
this.cacheTime = null;
this.cacheTimeout = 30000; // 30秒缓存
// 初始化注册文件路径
this.initRegistryPath();
}
}注册文件路径初始化
javascript
/**
* 初始化注册文件路径
*/
initRegistryPath() {
try {
// 检查是否在CEP环境中且Folder API可用
if (typeof Folder !== 'undefined' && Folder.temp) {
const tempFolder = Folder.temp;
if (tempFolder && tempFolder.fsName) {
this.registryFile = tempFolder.fsName + '/eagle2ae_port.txt';
this.log(`端口注册文件路径: ${this.registryFile}`, 'info');
return;
}
}
// 如果Folder API不可用,使用备用方案
throw new Error('Folder API不可用或无法获取临时目录');
} catch (error) {
this.log(`初始化注册文件路径失败: ${error.message}`, 'error');
// 使用备用路径(相对路径)
this.registryFile = 'eagle2ae_port.txt';
this.log(`使用备用注册文件路径: ${this.registryFile}`, 'warning');
}
}注册文件读取
javascript
/**
* 读取注册文件
* @returns {Object|null} 服务信息或null
*/
readRegistryFile() {
try {
// 检查File API是否可用
if (typeof File === 'undefined') {
this.log('File API不可用,跳过注册文件读取', 'warning');
return null;
}
// 跳过CEP环境中的File API,避免构造函数兼容性问题
// 直接返回null,让系统使用默认的端口发现机制
this.log('跳过注册文件读取,使用默认端口发现机制', 'info');
return null;
} catch (error) {
this.log(`读取注册文件失败: ${error.message}`, 'error');
return null;
}
}服务信息验证
javascript
/**
* 验证服务信息
* @param {Object} serviceInfo - 服务信息
* @returns {Object} 验证结果
*/
validateServiceInfo(serviceInfo) {
if (!serviceInfo) {
return { valid: false, reason: '服务信息为空' };
}
// 检查必要字段
if (!serviceInfo.port || !serviceInfo.pid || !serviceInfo.startTime) {
return { valid: false, reason: '服务信息格式不正确' };
}
// 检查端口范围
const port = parseInt(serviceInfo.port);
if (isNaN(port) || port < 1024 || port > 65535) {
return { valid: false, reason: `端口号无效: ${serviceInfo.port}` };
}
// 检查时间戳(超过1小时认为过期)
const now = Date.now();
const age = now - serviceInfo.startTime;
const maxAge = 60 * 60 * 1000; // 1小时
if (age > maxAge) {
return { valid: false, reason: '服务信息已过期' };
}
return {
valid: true,
port: port,
age: Math.floor(age / 1000) // 秒
};
}端口连接测试
javascript
/**
* 测试端口连接
* @param {number} port - 端口号
* @param {number} timeout - 超时时间(毫秒)
* @returns {Promise<Object>} 测试结果
*/
async testPortConnection(port, timeout = 3000) {
return new Promise((resolve) => {
const testUrl = `http://localhost:${port}/ping`;
// 创建超时处理
const timeoutId = setTimeout(() => {
resolve({ success: false, reason: '连接超时' });
}, timeout);
try {
// 使用fetch API测试连接
fetch(testUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
clearTimeout(timeoutId);
if (response.ok) {
return response.json();
} else {
throw new Error(`HTTP ${response.status}`);
}
})
.then(data => {
if (data && data.service === 'Eagle2Ae') {
resolve({ success: true, data: data });
} else {
resolve({ success: false, reason: '服务标识不匹配' });
}
})
.catch(error => {
resolve({ success: false, reason: error.message });
});
} catch (error) {
clearTimeout(timeoutId);
resolve({ success: false, reason: error.message });
}
});
}API参考
构造函数
javascript
/**
* 端口发现服务构造函数
* @param {Function} logger - 日志函数
*/
constructor(logger)核心方法
discoverPort()
发现Eagle服务端口
javascript
/**
* 发现Eagle服务端口
* @returns {Promise<Object>} 发现结果
*/
async discoverPort()getEaglePort()
获取Eagle端口(主要方法)
javascript
/**
* 获取Eagle端口(主要方法)
* @returns {Promise<number>} Eagle端口号
*/
async getEaglePort()clearCache()
清除缓存
javascript
/**
* 清除缓存
*/
clearCache()getDiscoveryStatus()
获取发现状态
javascript
/**
* 获取发现状态
* @returns {Promise<Object>} 发现状态信息
*/
async getDiscoveryStatus()辅助方法
readRegistryFile()
读取注册文件
javascript
/**
* 读取注册文件
* @returns {Object|null} 服务信息或null
*/
readRegistryFile()validateServiceInfo()
验证服务信息
javascript
/**
* 验证服务信息
* @param {Object} serviceInfo - 服务信息
* @returns {Object} 验证结果
*/
validateServiceInfo(serviceInfo)testPortConnection()
测试端口连接
javascript
/**
* 测试端口连接
* @param {number} port - 端口号
* @param {number} timeout - 超时时间(毫秒)
* @returns {Promise<Object>} 测试结果
*/
async testPortConnection(port, timeout = 3000)使用示例
基本使用
javascript
// 创建端口发现服务实例
const portDiscovery = new PortDiscovery(console.log);
// 发现Eagle服务端口
const port = await portDiscovery.getEaglePort();
console.log(`发现Eagle端口: ${port}`);
// 获取详细的发现状态
const status = await portDiscovery.getDiscoveryStatus();
console.log('端口发现状态:', status);端口验证
javascript
// 创建端口发现服务实例
const portDiscovery = new PortDiscovery(console.log);
// 执行端口发现
const discoveryResult = await portDiscovery.discoverPort();
if (discoveryResult.success) {
console.log(`✅ 成功发现端口: ${discoveryResult.port}`);
// 检查服务信息
if (discoveryResult.serviceInfo) {
console.log('服务信息:', discoveryResult.serviceInfo);
}
} else {
console.log(`❌ 端口发现失败: ${discoveryResult.reason}`);
// 使用备选端口
if (discoveryResult.fallbackPort) {
console.log(`使用备选端口: ${discoveryResult.fallbackPort}`);
}
}缓存管理
javascript
// 创建端口发现服务实例
const portDiscovery = new PortDiscovery(console.log);
// 获取端口(可能使用缓存)
const port1 = await portDiscovery.getEaglePort();
// 清除缓存
portDiscovery.clearCache();
// 再次获取端口(强制重新发现)
const port2 = await portDiscovery.getEaglePort();
console.log(`端口1: ${port1}, 端口2: ${port2}`);状态监控
javascript
// 创建端口发现服务实例
const portDiscovery = new PortDiscovery(console.log);
// 定期检查端口发现状态
setInterval(async () => {
try {
const status = await portDiscovery.getDiscoveryStatus();
console.log('端口发现状态更新:', {
discovered: status.discovered,
port: status.port,
source: status.source,
cached: status.cached
});
// 根据状态更新UI
updatePortStatusUI(status);
} catch (error) {
console.error('获取端口发现状态失败:', error);
}
}, 30000); // 每30秒检查一次最佳实践
性能优化
合理使用缓存
javascript// 利用缓存避免重复的端口发现 const portDiscovery = new PortDiscovery(); // 第一次调用可能执行实际发现 const port1 = await portDiscovery.getEaglePort(); // 短时间内再次调用将使用缓存 const port2 = await portDiscovery.getEaglePort(); console.log(`端口1: ${port1}, 端口2: ${port2} (来自缓存)`);设置合适的超时时间
javascript// 为不同的网络环境设置合适的超时时间 const portDiscovery = new PortDiscovery(); // 在局域网环境中使用较短超时 const result = await portDiscovery.testPortConnection(8080, 2000); // 在可能较慢的网络中使用较长超时 const result2 = await portDiscovery.testPortConnection(8080, 5000);批量状态检查
javascript// 批量获取端口发现状态以减少重复调用 async function getPortDiscoveryInfo(portDiscovery) { const [port, status] = await Promise.all([ portDiscovery.getEaglePort(), portDiscovery.getDiscoveryStatus() ]); return { port, status }; }
错误处理
统一错误处理
javascript// 为端口发现提供统一的错误处理 async function discoverEaglePort(portDiscovery) { try { const port = await portDiscovery.getEaglePort(); if (!port) { throw new Error('无法发现有效的Eagle端口'); } return port; } catch (error) { console.error('端口发现失败:', error.message); // 提供默认端口作为降级方案 return 8080; } }降级处理
javascript// 当端口发现失败时提供降级方案 const portDiscovery = new PortDiscovery(); const discoveryResult = await portDiscovery.discoverPort(); let eaglePort; if (discoveryResult.success) { eaglePort = discoveryResult.port; } else { // 降级到常用端口 eaglePort = discoveryResult.fallbackPort || 8080; console.warn(`端口发现失败,使用默认端口: ${eaglePort}`); }
内存管理
及时清理资源
javascript// 在应用关闭时清理端口发现服务 window.addEventListener('beforeunload', () => { if (portDiscovery) { // 清除定时器和其他资源 portDiscovery.clearCache(); } });避免内存泄漏
javascript// 在组件销毁时清理引用 function cleanupPortDiscovery(portDiscovery) { if (portDiscovery) { // 清除缓存 portDiscovery.clearCache(); // 清除引用 portDiscovery.logger = null; } }
故障排除
常见问题
端口发现失败
- 症状:无法发现Eagle服务端口
- 解决:
- 检查Eagle插件是否正在运行
- 验证注册文件是否存在
- 确认端口是否被防火墙阻止
- 检查端口是否被其他程序占用
注册文件读取失败
- 症状:控制台显示注册文件读取错误
- 解决:
- 检查临时目录权限
- 验证注册文件格式
- 确认File API可用性
- 检查文件路径是否正确
连接测试超时
- 症状:端口连接测试超时
- 解决:
- 检查网络连接状态
- 验证Eagle服务是否响应
- 调整超时设置
- 检查防火墙设置
调试技巧
启用详细日志
javascript
// 创建带详细日志的端口发现服务
const portDiscovery = new PortDiscovery((message, level) => {
console.log(`[PortDiscovery][${level}] ${message}`);
});
// 执行端口发现并查看详细日志
const result = await portDiscovery.discoverPort();性能分析
javascript
// 记录端口发现性能
async function analyzePortDiscoveryPerformance(portDiscovery) {
const startTime = performance.now();
const result = await portDiscovery.discoverPort();
const endTime = performance.now();
console.log(`端口发现耗时: ${endTime - startTime}ms`);
console.log('发现结果:', result);
return result;
}内存使用监控
javascript
// 监控端口发现服务内存使用
function logPortDiscoveryMemoryUsage(portDiscovery) {
console.log('端口发现服务状态:', {
hasCachedPort: !!portDiscovery.cachedPort,
cacheTime: portDiscovery.cacheTime,
registryFile: portDiscovery.registryFile
});
}扩展性
自定义验证规则
javascript
// 扩展端口发现服务以支持自定义验证
class CustomPortDiscovery extends PortDiscovery {
constructor(logger) {
super(logger);
this.customValidators = [];
}
/**
* 添加自定义验证器
* @param {Function} validator - 验证函数
*/
addCustomValidator(validator) {
this.customValidators.push(validator);
}
/**
* 重写验证方法以支持自定义验证
*/
async validateServiceInfo(serviceInfo) {
// 执行标准验证
const standardResult = super.validateServiceInfo(serviceInfo);
if (!standardResult.valid) {
return standardResult;
}
// 执行自定义验证
for (const validator of this.customValidators) {
try {
const customResult = await validator(serviceInfo);
if (!customResult.valid) {
return customResult;
}
} catch (error) {
this.log(`自定义验证器执行失败: ${error.message}`, 'error');
}
}
return standardResult;
}
}
// 使用自定义端口发现服务
const customPortDiscovery = new CustomPortDiscovery(console.log);
// 添加自定义验证规则
customPortDiscovery.addCustomValidator(async (serviceInfo) => {
// 检查特定的端口范围
if (serviceInfo.port < 8000 || serviceInfo.port > 9000) {
return { valid: false, reason: '端口不在允许范围内' };
}
return { valid: true };
});
// 执行端口发现
const port = await customPortDiscovery.getEaglePort();事件系统
javascript
// 为端口发现服务添加事件系统
class EventedPortDiscovery extends PortDiscovery {
constructor(logger) {
super(logger);
this.eventListeners = {};
}
/**
* 添加事件监听器
* @param {string} event - 事件名称
* @param {Function} callback - 回调函数
*/
on(event, callback) {
if (!this.eventListeners[event]) {
this.eventListeners[event] = [];
}
this.eventListeners[event].push(callback);
}
/**
* 触发事件
* @param {string} event - 事件名称
* @param {Object} data - 事件数据
*/
emit(event, data) {
if (this.eventListeners[event]) {
this.eventListeners[event].forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('事件回调执行失败:', error);
}
});
}
}
/**
* 重写发现方法以触发事件
*/
async discoverPort() {
this.emit('discovery:start', { timestamp: Date.now() });
try {
const result = await super.discoverPort();
if (result.success) {
this.emit('discovery:success', { port: result.port, result });
} else {
this.emit('discovery:failure', { reason: result.reason, result });
}
return result;
} catch (error) {
this.emit('discovery:error', { error });
throw error;
}
}
}
// 使用带事件的端口发现服务
const eventedPortDiscovery = new EventedPortDiscovery(console.log);
// 监听端口发现事件
eventedPortDiscovery.on('discovery:start', (data) => {
console.log('开始端口发现:', data.timestamp);
});
eventedPortDiscovery.on('discovery:success', (data) => {
console.log('端口发现成功:', data.port);
});
eventedPortDiscovery.on('discovery:failure', (data) => {
console.log('端口发现失败:', data.reason);
});
// 执行端口发现将触发相应事件
const port = await eventedPortDiscovery.getEaglePort();