##### Coverage Report - com.ctrip.framework.apollo.portal.service.ServiceLocator

Classes in this File | Line Coverage | Branch Coverage | Complexity
ServiceLocator |
71% | 27/38
31% | 5/16
| 3.8;3.8

1 |  |
     package com.ctrip.framework.apollo.portal.service;
2 |  |
3 |  |
     import java.net.URI;
4 |  |
     import java.util.Map;
5 |  |
     import java.util.concurrent.ConcurrentHashMap;
6 |  |
     import java.util.concurrent.atomic.AtomicInteger;
7 |  |
8 |  |
     import javax.annotation.PostConstruct;
9 |  |
10 |  |
     import org.slf4j.Logger;
11 |  |
     import org.slf4j.LoggerFactory;
12 |  |
     import org.springframework.beans.factory.annotation.Autowired;
13 |  |
     import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
14 |  |
     import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
15 |  |
     import org.springframework.http.client.SimpleClientHttpRequestFactory;
16 |  |
     import org.springframework.stereotype.Service;
17 |  |
     import org.springframework.web.client.RestTemplate;
18 |  |
19 |  |
     import com.ctrip.framework.apollo.core.MetaDomainConsts;
20 |  |
     import com.ctrip.framework.apollo.core.dto.ServiceDTO;
21 |  |
     import com.ctrip.framework.apollo.core.enums.Env;
22 |  |
     import com.ctrip.framework.apollo.core.exception.ServiceException;
23 |  |
24 |  |
     /**
25 |  |
      * @author liuym
26 |  |
      */
27 |  |
     @Service
28 | 1 |
     public class ServiceLocator {
29 |  |
30 | 1 |
       private static final Logger logger = LoggerFactory.getLogger(ServiceLocator.class);
31 |  |
32 |  |
       private static final int DEFAULT_TIMEOUT_MS = 1000;
33 |  |
34 |  |
       private static final int RETRY_TIMES = 3;
35 |  |
36 |  |
       private static final int CALL_META_SERVER_THRESHOLD = 2;
37 |  |
38 |  |
       private static final String ADMIN_SERVICE_URL_PATH = "/services/admin";
39 |  |
40 |  |
       private RestTemplate restTemplate;
41 |  |
42 |  |
       @Autowired
43 |  |
       private HttpMessageConverters httpMessageConverters;
44 |  |
45 | 1 |
       private Map<Env, ServiceDTO[]> serviceAddressCache = new ConcurrentHashMap<>();
46 |  |
47 | 1 |
       private final AtomicInteger adminCallCounts = new AtomicInteger(0);
48 |  |
49 |  |
       @PostConstruct
50 |  |
       private void postConstruct() {
51 | 1 |
         restTemplate = new RestTemplate(httpMessageConverters.getConverters());
52 | 1 |
         if (restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) {
53 | 1 |
           SimpleClientHttpRequestFactory rf =
54 | 1 |
               (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
55 | 1 |
           rf.setReadTimeout(DEFAULT_TIMEOUT_MS);
56 | 1 |
           rf.setConnectTimeout(DEFAULT_TIMEOUT_MS);
57 | 1 |
         } else if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) {
58 | 0 |
           HttpComponentsClientHttpRequestFactory rf =
59 | 0 |
               (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
60 | 0 |
           rf.setReadTimeout(DEFAULT_TIMEOUT_MS);
61 | 0 |
           rf.setConnectTimeout(DEFAULT_TIMEOUT_MS);
62 |  |
         }
63 | 1 |
       }
64 |  |
65 |  |
66 |  |
       public ServiceDTO getServiceAddress(Env env) throws ServiceException {
67 |  |
68 | 12 |
         if (adminCallCounts.get() % CALL_META_SERVER_THRESHOLD == 0) {
69 | 12 |
           return getServiceAddressFromMetaServer(env);
70 |  |
         } else {
71 |  |
           //if cached then return from cache
72 | 0 |
           ServiceDTO[] serviceDTOs = serviceAddressCache.get(env);
73 | 0 |
           if (serviceDTOs != null && serviceDTOs.length > 0){
74 | 0 |
             return randomServiceAddress(serviceDTOs);
75 |  |
           }else {//return from meta server
76 | 0 |
             return getServiceAddressFromMetaServer(env);
77 |  |
           }
78 |  |
         }
79 |  |
80 |  |
81 |  |
       }
82 |  |
83 |  |
       public ServiceDTO getServiceAddressFromMetaServer(Env env) {
84 |  |
         //retry
85 | 48 |
         for (int i = 0; i < RETRY_TIMES; i++) {
86 | 36 |
           ServiceDTO[] services = getServices(env);
87 | 36 |
           if (services != null && services.length > 0) {
88 | 0 |
             serviceAddressCache.put(env, services);
89 | 0 |
             return randomServiceAddress(services);
90 |  |
           } else {
91 | 36 |
             logger.warn(String.format("can not get %s admin service address at %d time", env, i));
92 |  |
           }
93 |  |
         }
94 |  |
         //clear cache
95 | 12 |
         serviceAddressCache.remove(env);
96 | 12 |
         logger.error(String.format("can not get %s admin service address", env));
97 | 12 |
         throw new ServiceException("No available admin service");
98 |  |
       }
99 |  |
100 |  |
101 |  |
       private ServiceDTO[] getServices(Env env) {
102 | 36 |
         String domainName = MetaDomainConsts.getDomain(env);
103 | 36 |
         String url = domainName + ADMIN_SERVICE_URL_PATH;
104 |  |
105 |  |
         try {
106 | 36 |
           return restTemplate.getForObject(new URI(url), ServiceDTO[].class);
107 | 36 |
         } catch (Exception ex) {
108 | 36 |
           logger.warn(ex.getMessage());
109 | 36 |
           return null;
110 |  |
         }
111 |  |
       }
112 |  |
113 |  |
       private ServiceDTO randomServiceAddress(ServiceDTO[] services){
114 | 0 |
         return services[Math.abs(adminCallCounts.getAndIncrement()) % services.length];
115 |  |
       }
116 |  |
117 |  |
     }