Skip to content

Commit

Permalink
replace http client implementation with interface (apolloconfig#3594)
Browse files Browse the repository at this point in the history
  • Loading branch information
vdiskg authored Mar 16, 2021
1 parent d6e0b01 commit 9f3d315
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
import com.ctrip.framework.apollo.util.http.HttpUtil;
import com.ctrip.framework.apollo.util.http.HttpClient;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
Expand All @@ -33,7 +33,7 @@

public class ConfigServiceLocator {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
private HttpUtil m_httpUtil;
private HttpClient m_httpClient;
private ConfigUtil m_configUtil;
private AtomicReference<List<ServiceDTO>> m_configServices;
private Type m_responseType;
Expand All @@ -49,7 +49,7 @@ public ConfigServiceLocator() {
m_configServices = new AtomicReference<>(initial);
m_responseType = new TypeToken<List<ServiceDTO>>() {
}.getType();
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
m_httpClient = ApolloInjector.getInstance(HttpClient.class);
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
this.m_executorService = Executors.newScheduledThreadPool(1,
ApolloThreadFactory.create("ConfigServiceLocator", true));
Expand Down Expand Up @@ -151,7 +151,7 @@ private synchronized void updateConfigServices() {
Transaction transaction = Tracer.newTransaction("Apollo.MetaService", "getConfigService");
transaction.addData("Url", url);
try {
HttpResponse<List<ServiceDTO>> response = m_httpUtil.doGet(request, m_responseType);
HttpResponse<List<ServiceDTO>> response = m_httpClient.doGet(request, m_responseType);
transaction.setStatus(Transaction.SUCCESS);
List<ServiceDTO> services = response.getBody();
if (services == null || services.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.http.HttpUtil;
import com.ctrip.framework.apollo.util.http.DefaultHttpClient;
import com.ctrip.framework.apollo.util.http.HttpClient;

import com.ctrip.framework.apollo.util.yaml.YamlParser;
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
Expand Down Expand Up @@ -83,7 +84,7 @@ protected void configure() {
bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
bind(ConfigFactory.class).to(DefaultConfigFactory.class).in(Singleton.class);
bind(ConfigUtil.class).in(Singleton.class);
bind(HttpUtil.class).in(Singleton.class);
bind(HttpClient.class).to(DefaultHttpClient.class).in(Singleton.class);
bind(ConfigServiceLocator.class).in(Singleton.class);
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
import com.ctrip.framework.apollo.util.http.HttpUtil;
import com.ctrip.framework.apollo.util.http.HttpClient;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
Expand Down Expand Up @@ -65,7 +65,7 @@ public class RemoteConfigLongPollService {
private Type m_responseType;
private static final Gson GSON = new Gson();
private ConfigUtil m_configUtil;
private HttpUtil m_httpUtil;
private HttpClient m_httpClient;
private ConfigServiceLocator m_serviceLocator;

/**
Expand All @@ -84,7 +84,7 @@ public RemoteConfigLongPollService() {
m_responseType = new TypeToken<List<ApolloConfigNotification>>() {
}.getType();
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
m_httpClient = ApolloInjector.getInstance(HttpClient.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
m_longPollRateLimiter = RateLimiter.create(m_configUtil.getLongPollQPS());
}
Expand Down Expand Up @@ -171,7 +171,7 @@ private void doLongPollingRefresh(String appId, String cluster, String dataCente
transaction.addData("Url", url);

final HttpResponse<List<ApolloConfigNotification>> response =
m_httpUtil.doGet(request, m_responseType);
m_httpClient.doGet(request, m_responseType);

logger.debug("Long polling response: {}, url: {}", response.getStatusCode(), url);
if (response.getStatusCode() == 200 && response.getBody() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
import com.ctrip.framework.apollo.util.http.HttpUtil;
import com.ctrip.framework.apollo.util.http.HttpClient;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
Expand Down Expand Up @@ -52,7 +52,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();

private final ConfigServiceLocator m_serviceLocator;
private final HttpUtil m_httpUtil;
private final HttpClient m_httpClient;
private final ConfigUtil m_configUtil;
private final RemoteConfigLongPollService remoteConfigLongPollService;
private volatile AtomicReference<ApolloConfig> m_configCache;
Expand All @@ -79,7 +79,7 @@ public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
m_configCache = new AtomicReference<>();
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
m_httpClient = ApolloInjector.getInstance(HttpClient.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
m_longPollServiceDto = new AtomicReference<>();
Expand Down Expand Up @@ -218,7 +218,7 @@ private ApolloConfig loadApolloConfig() {
transaction.addData("Url", url);
try {

HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
HttpResponse<ApolloConfig> response = m_httpClient.doGet(request, ApolloConfig.class);
m_configNeedForceRefresh.set(false);
m_loadConfigFailSchedulePolicy.success();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.ctrip.framework.apollo.util.http;

import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.apollo.exceptions.ApolloConfigStatusCodeException;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.google.common.base.Function;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
* @author Jason Song([email protected])
*/
public class DefaultHttpClient implements HttpClient {
private ConfigUtil m_configUtil;
private static final Gson GSON = new Gson();

/**
* Constructor.
*/
public DefaultHttpClient() {
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
}

/**
* Do get operation for the http request.
*
* @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws ApolloConfigException if any error happened or response code is neither 200 nor 304
*/
@Override
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, final Class<T> responseType) {
Function<String, T> convertResponse = new Function<String, T>() {
@Override
public T apply(String input) {
return GSON.fromJson(input, responseType);
}
};

return doGetWithSerializeFunction(httpRequest, convertResponse);
}

/**
* Do get operation for the http request.
*
* @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws ApolloConfigException if any error happened or response code is neither 200 nor 304
*/
@Override
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, final Type responseType) {
Function<String, T> convertResponse = new Function<String, T>() {
@Override
public T apply(String input) {
return GSON.fromJson(input, responseType);
}
};

return doGetWithSerializeFunction(httpRequest, convertResponse);
}

private <T> HttpResponse<T> doGetWithSerializeFunction(HttpRequest httpRequest,
Function<String, T> serializeFunction) {
InputStreamReader isr = null;
InputStreamReader esr = null;
int statusCode;
try {
HttpURLConnection conn = (HttpURLConnection) new URL(httpRequest.getUrl()).openConnection();

conn.setRequestMethod("GET");

Map<String, String> headers = httpRequest.getHeaders();
if (headers != null && headers.size() > 0) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
}

int connectTimeout = httpRequest.getConnectTimeout();
if (connectTimeout < 0) {
connectTimeout = m_configUtil.getConnectTimeout();
}

int readTimeout = httpRequest.getReadTimeout();
if (readTimeout < 0) {
readTimeout = m_configUtil.getReadTimeout();
}

conn.setConnectTimeout(connectTimeout);
conn.setReadTimeout(readTimeout);

conn.connect();

statusCode = conn.getResponseCode();
String response;

try {
isr = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8);
response = CharStreams.toString(isr);
} catch (IOException ex) {
/**
* according to https://docs.oracle.com/javase/7/docs/technotes/guides/net/http-keepalive.html,
* we should clean up the connection by reading the response body so that the connection
* could be reused.
*/
InputStream errorStream = conn.getErrorStream();

if (errorStream != null) {
esr = new InputStreamReader(errorStream, StandardCharsets.UTF_8);
try {
CharStreams.toString(esr);
} catch (IOException ioe) {
//ignore
}
}

// 200 and 304 should not trigger IOException, thus we must throw the original exception out
if (statusCode == 200 || statusCode == 304) {
throw ex;
}
// for status codes like 404, IOException is expected when calling conn.getInputStream()
throw new ApolloConfigStatusCodeException(statusCode, ex);
}

if (statusCode == 200) {
return new HttpResponse<>(statusCode, serializeFunction.apply(response));
}

if (statusCode == 304) {
return new HttpResponse<>(statusCode, null);
}
} catch (ApolloConfigStatusCodeException ex) {
throw ex;
} catch (Throwable ex) {
throw new ApolloConfigException("Could not complete get operation", ex);
} finally {
if (isr != null) {
try {
isr.close();
} catch (IOException ex) {
// ignore
}
}

if (esr != null) {
try {
esr.close();
} catch (IOException ex) {
// ignore
}
}
}

throw new ApolloConfigStatusCodeException(statusCode,
String.format("Get operation failed for %s", httpRequest.getUrl()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.ctrip.framework.apollo.util.http;

import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import java.lang.reflect.Type;

/**
* @author Jason Song([email protected])
*/
public interface HttpClient {

/**
* Do get operation for the http request.
*
* @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws ApolloConfigException if any error happened or response code is neither 200 nor 304
*/
<T> HttpResponse<T> doGet(HttpRequest httpRequest, final Class<T> responseType)
throws ApolloConfigException;

/**
* Do get operation for the http request.
*
* @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws ApolloConfigException if any error happened or response code is neither 200 nor 304
*/
<T> HttpResponse<T> doGet(HttpRequest httpRequest, final Type responseType)
throws ApolloConfigException;
}
Loading

0 comments on commit 9f3d315

Please sign in to comment.