背景
在开发帕秋莉(基于 OpenResty & Node.js 的网关系统)时,需要解析域名并转发请求到真实业务服务器上去,帕秋莉支持 *.xxx.com 这样的通配域名,所以涉及到解析包含层级关系的子域名。
思路
实现一棵树,树的每个节点存储域名对应的信息,子节点存储子域名对应的信息。

节点
| 字段 | 类型 | 含义 | 示例 |
|---|---|---|---|
| id | String | ID | 0pzPEiCTR1V |
| value | String | 域名 | *.xxx.com |
| isWildcard | Bool | 是否为通配域名 | true |
| accessControlAllowOrigin | String | 允许跨域的域名 | * |
| appId | String | 业务服务器 ID | bEYO9iCTRoM |
| regexp | String | 域名正则表达式 | /^((?:.*)).xxx.com(?:/(?= |
| children | Array | 子节点 |
代码
实现
class DNS {
constructor(domain) {
// 节点
this.id = domain.id;
this.value = domain.value;
this.isWildcard = domain.isWildcard;
this.accessControlAllowOrigin = domain.accessControlAllowOrigin;
this.appId = domain.appId;
this.regexp = pathToRegexp(domain.value);
this.children = [];
}
init(domain) {
// 新节点与当前节点不存在主子关系
if(!this.regexp.exec(domain.value)) return false;
// 尝试向当前节点的所有自己点添加新节点
let result = false;
for(const child of this.children) {
result = child.init(domain);
if(result) break;
}
// 添加失败新节点是当前子节点的兄弟节点
if(!result) {
this.children.push(domain);
result = true;
}
return result;
}
parse(domain) {
// 需要解析的节点与当前节点不存在包含关系
if(!this.regexp.exec(domain.value)) return null;
// 尝试在当前节点的所有自己点中解析
let result = null;
for(const child of this.children) {
result = child.parse(domain);
if(result) break;
}
// 解析失败代表需要解析的节点不存在与当前节点的子节点中
// 满足当前节点的正则,不满足当前节点所有子节点的正则,需要解析的节点即为当前节点
if(!result) {
result = {
id: this.id,
value: this.value,
isWildcard: this.isWildcard,
accessControlAllowOrigin: this.accessControlAllowOrigin,
appId: this.appId,
regexp: this.regexp,
children: this.children
};
}
return result;
}
}
使用
// 使用 * 创建一个 root 节点
const dns = new DNS({ value: * });
dns.init({
id: '0pzPEiCTR1V',
value: '*.xxx.com',
isWildcard: true,
accessControlAllowOrigin: '*',
appId: bEYO9iCTRoM
});
// 利用域名解析节点详细信息
const result = dns.parse({
value: '*.xxx.com'
});
console.log(result);