遗留非springboot传统项目接入eureka注册与服务发现

Posted 伯约听风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了遗留非springboot传统项目接入eureka注册与服务发现相关的知识,希望对你有一定的参考价值。

推荐: jeesuite开发框架,免费开源、一站式解决方案。

最近规划自动化运维以及统一监控需求,鉴于目前公司内部大部分项目采用spring cloud体系架构、另外还有一些老的传统spring web的项目,于是就考虑把老的项目通过低成本改造的方式接入spring cloud体系,也就是可以通过eureka注册和服务发现、通过zuul服务路由。

说干就干,通过eureka官方实例和研究spring boot注册eureka源码发现这个也很容易实现,所以废话不多说,直接贴代码了 。

首先加入项目依赖(maven为例)
<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
  • spring.cloud.client.ipAddress :为自定义变量
  • healthCheckUrl,statusPageUrl接口可以不要,但是为了监控可以自己实现一个简单的接口即可
初始化eureka客户端
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转发配置
zuul.routes.demo.path=/demo/**
zuul.routes.demo.serviceId=demo-service

到此,服务注册和服务发现都完成了。
这里是一个demo

有任何问题,请加技术群:230192763讨论

以上是关于遗留非springboot传统项目接入eureka注册与服务发现的主要内容,如果未能解决你的问题,请参考以下文章

原来的SpringBoot项目注册到Eureka上

springboot grpc eureka demo实战项目

Docker部署springboot,从简单Eureka开始

Java之 Spring Cloud 微服务 Eureka (第一个阶段)SpringBoot项目实现商品服务器端是调用

Springboot整合eureka

传统SSH项目集成到Springcloud