euraka集群JerseyReplicationClient找不到证书问题
源码分析如下:
eureka服务器需要在启动类上加上@EnableEurekaServer注解,改类会自动的加载一个配置类 EurekaServerAutoConfiguration,其创建集群节点代码分析如下:
RefreshablePeerEurekaNodes 类如下:
在createPeerEurekaNode方法里面,为每个节点创建备份客户端JerseyReplicationClient,具体的创建方法如下
public static JerseyReplicationClient createReplicationClient(EurekaServerConfig config, ServerCodecs serverCodecs, String serviceUrl) {
String name = JerseyReplicationClient.class.getSimpleName() + ": " + serviceUrl + "apps/: ";
EurekaJerseyClient jerseyClient;
try {
String hostname;
try {
hostname = new URL(serviceUrl).getHost();
} catch (MalformedURLException e) {
hostname = serviceUrl;
}
String jerseyClientName = "Discovery-PeerNodeClient-" + hostname;
EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientBuilder()
.withClientName(jerseyClientName)
.withUserAgent("Java-EurekaClient-Replication")
.withEncoderWrapper(serverCodecs.getFullJsonCodec())
.withDecoderWrapper(serverCodecs.getFullJsonCodec())
.withConnectionTimeout(config.getPeerNodeConnectTimeoutMs())
.withReadTimeout(config.getPeerNodeReadTimeoutMs())
.withMaxConnectionsPerHost(config.getPeerNodeTotalConnectionsPerHost())
.withMaxTotalConnections(config.getPeerNodeTotalConnections())
.withConnectionIdleTimeout(config.getPeerNodeConnectionIdleTimeoutSeconds());
发现工具类EurekaJerseyClientBuilder自定义备份客户端的实现
具体的代码实现:
/**
* 功能描述
*
* @since 2022-11-08
*/
@Configuration
@EnableConfigurationProperties(ClientSslProperty.class)
public class EurekaServerConfigTest {
private static final Logger LOGGER = LoggerFactory.getLogger(EurekaServerConfigMy.class);
@Autowired
private ApplicationInfoManager applicationInfoManager;
@Autowired
private EurekaServerConfig eurekaServerConfig;
@Autowired
private EurekaClientConfig eurekaClientConfig;
@Autowired
private ClientSslProperty clientSslProperty;
@Autowired
private AesCiperServiceUtil aesCiperService;
/**
* 功能描述:向容器中注入自己的PeerEurekaNodes
*
* @param registry 与Eureka中registry相关的接口
* @param serverCodecs Eureka的serverCodecs接口
* @return 返回PeerEurekaNodes
*/
@Bean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) {
RefreshablePeerEurekaNodes peerEurekaNodesRefreshable = new RefreshablePeerEurekaNodes(registry,
this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
peerEurekaNodesRefreshable.setClientSslProperty(clientSslProperty);
peerEurekaNodesRefreshable.setAesCiperService(aesCiperService);
return peerEurekaNodesRefreshable;
}
/**
* Refresh Peer Eureka Nodes
*
* @since 2022-04-25
*/
static class RefreshablePeerEurekaNodes extends PeerEurekaNodes
implements ApplicationListener<EnvironmentChangeEvent> {
private ClientSslProperty clientSslProperty;
private AesCiperServiceUtil aesCiperService;
RefreshablePeerEurekaNodes(final PeerAwareInstanceRegistry registry, final EurekaServerConfig serverConfig,
final EurekaClientConfig clientConfig, final ServerCodecs serverCodecs,
final ApplicationInfoManager applicationInfoManager) {
super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager);
}
public void setClientSslProperty(final ClientSslProperty clientSslProperty) {
this.clientSslProperty = clientSslProperty;
}
public void setAesCiperService(AesCiperServiceUtil aesCiperService) {
this.aesCiperService = aesCiperService;
}
@Override
public void onApplicationEvent(final EnvironmentChangeEvent event) {
if (shouldUpdateNodes(event.getKeys())) {
updatePeerEurekaNodes(resolvePeerUrls());
}
}
/**
* 功能描述:Check whether specific properties have changed.
*
* @param keys event key的集合
* @return 是否需要更新节点
*/
private boolean shouldUpdateNodes(final Set<String> keys) {
if (keys == null) {
CodeCCUtils.warn(LOGGER, "keys is null.");
return false;
}
if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
return false;
}
if (keys.contains("eureka.client.region")) {
return true;
}
return keys.stream()
.filter(Objects::nonNull)
.anyMatch(key -> key.startsWith("eureka.client.service-url.")
|| key.startsWith("eureka.client.availability-zones."));
}
@Override
protected PeerEurekaNode createPeerEurekaNode(String peerEurekaNodeUrl) {
String targetHost = "host";
HttpReplicationClient replicationClient = null;
try {
CodeCCUtils.info(LOGGER, "create peer eureka node by SSL.");
replicationClient = createReplicationClient(serverConfig, serverCodecs, peerEurekaNodeUrl,
clientSslProperty, aesCiperService);
targetHost = hostFromUrl(peerEurekaNodeUrl);
if (targetHost == null) {
targetHost = "host";
}
} catch (UnrecoverableKeyException | CertificateOperationException e) {
CodeCCUtils.errorWithParamters(LOGGER, "Create peer eureka node error. {}", e);
}
return new PeerEurekaNode(registry, targetHost, peerEurekaNodeUrl, replicationClient, serverConfig);
}
/**
* Create Replication Client
*
* @param config Eureka的配置
* @param serverCodecs Eureka的serverCodecs接口
* @param serviceUrl 服务的url
* @param clientSslProperty ssl相关配置
* @param aesCiperService AES加解密组件
* @return Jersey Replication Client
* @throws UnrecoverableKeyException Unrecoverable Key 异常
* @throws CertificateOperationException 证书操作异常
*/
private static JerseyReplicationClient createReplicationClient(EurekaServerConfig config,
ServerCodecs serverCodecs, String serviceUrl, ClientSslProperty clientSslProperty,
AesCiperServiceUtil aesCiperService) throws UnrecoverableKeyException, CertificateOperationException {
String hostname;
try {
hostname = new URL(serviceUrl).getHost();
} catch (MalformedURLException e) {
hostname = serviceUrl;
}
// 加上SSL的配置
String keyStorePwd = aesCiperService.decryptStr(clientSslProperty.getKeyStorePassword());
String trustStorePwd = aesCiperService.decryptStr(clientSslProperty.getTrustStorePassword());
SSLContext sslContext;
try {
sslContext = SSLUtil.getSSLContext(clientSslProperty.getKeyStore(), keyStorePwd,
clientSslProperty.getTrustStore(), trustStorePwd);
} finally {
keyStorePwd = null;
trustStorePwd = null;
}
String jerseyClientName = "Discovery-PeerNodeClient-" + hostname;
EurekaJerseyClientImpl.EurekaJerseyClientBuilder clientBuilder =
new EurekaJerseyClientImpl.EurekaJerseyClientBuilder().withClientName(jerseyClientName)
.withUserAgent("Java-EurekaClient-Replication")
.withEncoderWrapper(serverCodecs.getFullJsonCodec())
.withDecoderWrapper(serverCodecs.getFullJsonCodec())
.withCustomSSL(sslContext)
.withConnectionTimeout(config.getPeerNodeConnectTimeoutMs())
.withReadTimeout(config.getPeerNodeReadTimeoutMs())
.withMaxConnectionsPerHost(config.getPeerNodeTotalConnectionsPerHost())
.withMaxTotalConnections(config.getPeerNodeTotalConnections())
.withConnectionIdleTimeout(config.getPeerNodeConnectionIdleTimeoutSeconds());
clientBuilder.withHostnameVerifier(HostnameVerifierUtil.getHostnameVerifier());
String ip = null;
try {
Optional<InetAddress> inetAddress = getLocalHostExactAddress();
if (inetAddress.isPresent()) {
ip = inetAddress.get().getHostAddress();
} else {
CodeCCUtils.warn(LOGGER, "get inetAddress is null.");
}
} catch (SocketException e) {
CodeCCUtils.errorWithParamters(LOGGER, "Cannot find localhost ip. {}", e);
}
EurekaJerseyClient jerseyClient = clientBuilder.build();
ApacheHttpClient4 jerseyApacheClient = jerseyClient.getClient();
jerseyApacheClient.addFilter(new DynamicGZIPContentEncodingFilter(config));
EurekaServerIdentity identity = new EurekaServerIdentity(ip);
jerseyApacheClient.addFilter(new EurekaIdentityHeaderFilter(identity));
return new JerseyReplicationClient(jerseyClient, serviceUrl);
}
private static Optional<InetAddress> getLocalHostExactAddress() throws SocketException {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
if (networkInterfaces == null) {
return Optional.empty();
}
InetAddress candidateAddress = null;
while (networkInterfaces.hasMoreElements()) {
NetworkInterface iface = networkInterfaces.nextElement();
ResponseVO<InetAddress> responseVo = getInetAddress(iface, candidateAddress);
if (responseVo.getResultCode() == ResultCodeEnum.SUCCEED.getValue()) {
return Optional.ofNullable(responseVo.getData());
}
candidateAddress = responseVo.getData();
}
// 如果出去loopback回环地之外无其它地址了,那就回退到原始方案吧
return Optional.ofNullable(candidateAddress);
}
private static ResponseVO<InetAddress> getInetAddress(NetworkInterface iface, InetAddress candidateAddress) {
InetAddress temp = candidateAddress;
ResponseVO<InetAddress> responseVo = new ResponseVO<>(ResultCodeEnum.SUCCEED.getValue());
for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
InetAddress inetAddr = inetAddrs.nextElement();
// 排除loopback回环类型地址(不管是IPv4还是IPv6 只要是回环地址都会返回true)
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址,就是它了 就是我们要找的
// 绝大部分情况下都会在此处返回你的ip地址值
responseVo.setData(inetAddr);
return responseVo;
}
// 若不是site-local地址 那就记录下该地址当作候选
if (temp == null) {
temp = inetAddr;
}
}
}
responseVo.setResultCode(ResultCodeEnum.FAILED.getValue());
responseVo.setData(temp);
return responseVo;
}
}
}
更多内容关注微信公众号 ”前后端技术精选“,或者语雀,里面有更多知识:www.yuque.com/riverzmm/uu… 《安全》