最近规划自动化运维以及统一监控需求,鉴于目前公司内部大部分项目采用spring cloud体系架构、另外还有一些老的传统spring web的项目,于是就考虑把老的项目通过低成本改造的方式接入spring cloud体系,也就是可以通过eureka注册和服务发现、通过zuul服务路由。
说干就干,通过eureka官方实例和研究spring boot注册eureka源码发现这个也很容易实现,所以废话不多说,直接贴代码了 。
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
<version>1.4.12</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.4</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-core</artifactId>
<version>1.2.6.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
eureka.region=default
eureka.registration.enabled=true
eureka.preferSameZone=true
eureka.shouldUseDns=false
eureka.serviceUrl.default=http://192.168.1.100:7861/eureka
eureka.decoderName=JacksonJson
eureka.name=demo
eureka.vipAddress=${eureka.name}-service
eureka.port=8081
eureka.homePageUrl=http://192.168.1.101:${eureka.port}
eureka.healthCheckUrl=http://192.168.1.101:${eureka.port}/service/health
eureka.statusPageUrl=http://192.168.1.101:${eureka.port}/service/info
private void initEurekaClient() throws Exception{
Properties properties = new Properties();
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("eureka.properties");
properties.load(inputStream);
//
properties.setProperty("eureka.ipAddr", IpUtils.getLocalIpAddr());
instanceId = properties.getProperty("eureka.ipAddr") + ":" + properties.getProperty("eureka.ipAddr") + "/" + properties.getProperty("eureka.name");
properties.setProperty("eureka.instanceId", instanceId);
ConfigurationManager.loadProperties(properties);
MyDataCenterInstanceConfig instanceConfig = new MyDataCenterInstanceConfig();
InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);
DefaultEurekaClientConfig clientConfig = new DefaultEurekaClientConfig();
eurekaClient = new DiscoveryClient(applicationInfoManager, clientConfig);
}
private void waitForRegistrationWithEureka() {
applicationInfoManager.setInstanceStatus(InstanceInfo.InstanceStatus.STARTING);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
applicationInfoManager.setInstanceStatus(InstanceInfo.InstanceStatus.UP);
long startTime = System.currentTimeMillis();
//开启一个线程验证注册结果
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (System.currentTimeMillis() - startTime > VERIFY_WAIT_MILLIS) {
log.warn(" >>>> service registration status not verify,please check it!!!!");
return;
}
try {
List<InstanceInfo> serverInfos = eurekaClient.getInstancesByVipAddress(vipAddress, false);
for (InstanceInfo nextServerInfo : serverInfos) {
if (nextServerInfo.getIPAddr().equals(IpUtils.LOCAL_BACK_IP)
|| nextServerInfo.getIPAddr().equals(IpUtils.getLocalIpAddr())) {
String instanceInfoJson = JsonUtils.getMapper().writerWithDefaultPrettyPrinter()
.writeValueAsString(nextServerInfo);
log.info("verifying service registration with eureka finished,instance:\n{}",
instanceInfoJson);
return;
}
}
} catch (Throwable e) {
}
try {
Thread.sleep(5000);
} catch (Exception e1) {
}
log.info("Waiting 5s... verifying service registration with eureka ...");
}
}
}).start();
}
通过这几步就完成了eureka的注册,登录eureka控制台你将能看到对应注册信息。但是在zuul转发调用过程发现一个问题:无法识别hostname,如果你们的服务器之间没有做hostname同步就需要继续改造,于是就看了下springboot注册eureka有一个配置项eureka.instance.preferIpAddress,所以我们也可以模仿他的实现。于是在初始化客户端的时候我们需要这样改造:
MyDataCenterInstanceConfig instanceConfig = new MyDataCenterInstanceConfig(){
@Override
public String getHostName(boolean refresh) {
String hostName = super.getHostName(refresh);
if(ResourceUtils.getBoolean("eureka.preferIpAddress")){
hostName = IpUtils.getLocalIpAddr();
}
return hostName;
}
@Override
public String getIpAddress() {
return IpUtils.getLocalIpAddr();
}
};
这样,注册的真实服务地址就是ip了。服务注册就搞定收工了。
接下来就是服务发现,及与其他springboot项目一样通过注册中心vipAddress互相调用。实际过程就是调用前去eureka拿一个真实地址替换vipAddress变量。
获取真实服务地址
public String getRealServerHost(String serviceId){
InstanceInfo serverInfo = eurekaClient.getNextServerFromEureka(serviceId, false);
String realServerName = serverInfo.getIPAddr() + ":" + serverInfo.getPort();
return realServerName;
}
下面是我实现的几个resttemplate
public class EurekaRestTemplateBuilder {
private static Map<String, RestTemplate> restTemplates = new HashMap<>();
public static synchronized RestTemplate build(ClientHttpRequestInterceptor ...interceptors ){
return build("default", interceptors);
}
public static synchronized RestTemplate build(String name,ClientHttpRequestInterceptor ...interceptors ){
if(restTemplates.containsKey(name))return restTemplates.get(name);
SimpleClientHttpRequestFactory factory = new EurekaClientHttpRequestFactory();
factory.setReadTimeout(15000);//ms
factory.setConnectTimeout(5000);//ms
RestTemplate restTemplate = new RestTemplate(factory);
List<ClientHttpRequestInterceptor> interceptorList = new ArrayList<>();
interceptorList.add(new RestTemplateAutoHeaderInterceptor());
if(interceptors != null && interceptors.length > 0){
for (ClientHttpRequestInterceptor interceptor : interceptors) {
interceptorList.add(interceptor);
}
}
restTemplate.setInterceptors(interceptorList);
//
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
//
restTemplates.put(name, restTemplate);
return restTemplate;
}
private static class EurekaClientHttpRequestFactory extends SimpleClientHttpRequestFactory{
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
uri = convertToRealUri(uri);
return super.createRequest(uri, httpMethod);
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
uri = convertToRealUri(uri);
return super.createAsyncRequest(uri, httpMethod);
}
private URI convertToRealUri(URI uri){
String serviceId = uri.getHost();
try {
String realHost = EurekaRegistry.getInstance().getRealServerHost(serviceId);
uri = new URI(uri.toString().replace(serviceId, realHost));
return uri;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
接下来就可以调用其他eureka注册服务了。
private RestTemplate restTemplate = EurekaRestTemplateBuilder.build();
public List<IdNamePair> getProvinces() {
ParameterizedTypeReference<List<IdNamePair>> arearesponseType = new ParameterizedTypeReference<List<IdNamePair>>() {
};
List<IdNamePair> lists = restTemplate
.exchange("http://DEMO-SERVICE/region/provinces", HttpMethod.GET, null, arearesponseType)
.getBody();
return lists;
}
zuul.routes.demo.path=/demo/**
zuul.routes.demo.serviceId=demo-service
到此,服务注册和服务发现都完成了。 这里是一个demo
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有