|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { ChannelOptions } from './channel-options'; |
|
import { SubchannelAddress } from './subchannel-address'; |
|
import { ConnectivityState } from './connectivity-state'; |
|
import { Picker } from './picker'; |
|
import { ChannelRef, SubchannelRef } from './channelz'; |
|
import { SubchannelInterface } from './subchannel-interface'; |
|
|
|
|
|
|
|
|
|
|
|
export interface ChannelControlHelper { |
|
|
|
|
|
|
|
|
|
|
|
createSubchannel( |
|
subchannelAddress: SubchannelAddress, |
|
subchannelArgs: ChannelOptions |
|
): SubchannelInterface; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateState(connectivityState: ConnectivityState, picker: Picker): void; |
|
|
|
|
|
|
|
requestReresolution(): void; |
|
addChannelzChild(child: ChannelRef | SubchannelRef): void; |
|
removeChannelzChild(child: ChannelRef | SubchannelRef): void; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function createChildChannelControlHelper( |
|
parent: ChannelControlHelper, |
|
overrides: Partial<ChannelControlHelper> |
|
): ChannelControlHelper { |
|
return { |
|
createSubchannel: |
|
overrides.createSubchannel?.bind(overrides) ?? |
|
parent.createSubchannel.bind(parent), |
|
updateState: |
|
overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent), |
|
requestReresolution: |
|
overrides.requestReresolution?.bind(overrides) ?? |
|
parent.requestReresolution.bind(parent), |
|
addChannelzChild: |
|
overrides.addChannelzChild?.bind(overrides) ?? |
|
parent.addChannelzChild.bind(parent), |
|
removeChannelzChild: |
|
overrides.removeChannelzChild?.bind(overrides) ?? |
|
parent.removeChannelzChild.bind(parent), |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export interface LoadBalancer { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateAddressList( |
|
addressList: SubchannelAddress[], |
|
lbConfig: LoadBalancingConfig, |
|
attributes: { [key: string]: unknown } |
|
): void; |
|
|
|
|
|
|
|
exitIdle(): void; |
|
|
|
|
|
|
|
|
|
|
|
resetBackoff(): void; |
|
|
|
|
|
|
|
|
|
destroy(): void; |
|
|
|
|
|
|
|
|
|
|
|
getTypeName(): string; |
|
} |
|
|
|
export interface LoadBalancerConstructor { |
|
new (channelControlHelper: ChannelControlHelper): LoadBalancer; |
|
} |
|
|
|
export interface LoadBalancingConfig { |
|
getLoadBalancerName(): string; |
|
toJsonObject(): object; |
|
} |
|
|
|
export interface LoadBalancingConfigConstructor { |
|
|
|
new (...args: any): LoadBalancingConfig; |
|
|
|
createFromJson(obj: any): LoadBalancingConfig; |
|
} |
|
|
|
const registeredLoadBalancerTypes: { |
|
[name: string]: { |
|
LoadBalancer: LoadBalancerConstructor; |
|
LoadBalancingConfig: LoadBalancingConfigConstructor; |
|
}; |
|
} = {}; |
|
|
|
let defaultLoadBalancerType: string | null = null; |
|
|
|
export function registerLoadBalancerType( |
|
typeName: string, |
|
loadBalancerType: LoadBalancerConstructor, |
|
loadBalancingConfigType: LoadBalancingConfigConstructor |
|
) { |
|
registeredLoadBalancerTypes[typeName] = { |
|
LoadBalancer: loadBalancerType, |
|
LoadBalancingConfig: loadBalancingConfigType, |
|
}; |
|
} |
|
|
|
export function registerDefaultLoadBalancerType(typeName: string) { |
|
defaultLoadBalancerType = typeName; |
|
} |
|
|
|
export function createLoadBalancer( |
|
config: LoadBalancingConfig, |
|
channelControlHelper: ChannelControlHelper |
|
): LoadBalancer | null { |
|
const typeName = config.getLoadBalancerName(); |
|
if (typeName in registeredLoadBalancerTypes) { |
|
return new registeredLoadBalancerTypes[typeName].LoadBalancer( |
|
channelControlHelper |
|
); |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
export function isLoadBalancerNameRegistered(typeName: string): boolean { |
|
return typeName in registeredLoadBalancerTypes; |
|
} |
|
|
|
export function getFirstUsableConfig( |
|
configs: LoadBalancingConfig[], |
|
fallbackTodefault?: true |
|
): LoadBalancingConfig; |
|
export function getFirstUsableConfig( |
|
configs: LoadBalancingConfig[], |
|
fallbackTodefault = false |
|
): LoadBalancingConfig | null { |
|
for (const config of configs) { |
|
if (config.getLoadBalancerName() in registeredLoadBalancerTypes) { |
|
return config; |
|
} |
|
} |
|
if (fallbackTodefault) { |
|
if (defaultLoadBalancerType) { |
|
return new registeredLoadBalancerTypes[ |
|
defaultLoadBalancerType |
|
]!.LoadBalancingConfig(); |
|
} else { |
|
return null; |
|
} |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
export function validateLoadBalancingConfig(obj: any): LoadBalancingConfig { |
|
if (!(obj !== null && typeof obj === 'object')) { |
|
throw new Error('Load balancing config must be an object'); |
|
} |
|
const keys = Object.keys(obj); |
|
if (keys.length !== 1) { |
|
throw new Error( |
|
'Provided load balancing config has multiple conflicting entries' |
|
); |
|
} |
|
const typeName = keys[0]; |
|
if (typeName in registeredLoadBalancerTypes) { |
|
return registeredLoadBalancerTypes[ |
|
typeName |
|
].LoadBalancingConfig.createFromJson(obj[typeName]); |
|
} else { |
|
throw new Error(`Unrecognized load balancing config name ${typeName}`); |
|
} |
|
} |
|
|