1.现状介绍
jenkins构建项目部署在长乐环境,由于长乐环境与AWS、AWS-CALIFORNIA环境网络很不稳定,导致经常请求AWS、AWS-CALIFORNIA环境服务时报超时错误,很是影响项目打包构建。
来看下,各环境网络互通情况:
可以看到,目前只有无锡环境与AWS、AWS-CALIFORNIA环境网络稳定。所以,我们可以通过访问无锡环境去做AWS代理转发,从而实现访问AWS、AWS-CALIFORNIA环境服务正常。
2.解决方案
在无锡环境部署一套代理服务,用于专门代理转发AWS外站。这样,长乐环境的项目要访问AWS外站服务,就可以通过访问无锡环境的代理服务,从而实现访问AWS外站。
我们再来看下,现在各环境网络互通情况:
3.代理模式
- 代理服务
request:
GET|POST|PUT https://xxx/proxy
headers:
APF-HEADER-ACTUAL-URL : 实际访问的地址
- 客户端
部署在长乐环境的jenkins项目,在构建的时候,不仅会访问无锡环境的服务,而且会访问AWS、AWS-CALIFORNIA环境服务。在做客户端统一调用的时候,考虑到访问AWS、AWS-CALIFORNIA环境服务需要访问无锡环境的代理服务,为了不破坏原来访问无锡环境服务的方式,只需要判断所请求的服务是否为AWS、AWS-CALIFORNIA环境服务,然后通过代理服务做跳转即可。所以我们想到了代理模式,即CGLIB动态代理。
CGLIB动态代理类:
import hudson.EnvVars;
import java.lang.reflect.Method;
import net.sf.json.JSONArray;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.alibaba.fastjson.JSONObject;
public class HttpClientProxy implements MethodInterceptor {
private EnvVars envVars;
/**
* @param envVars
*/
public HttpClientProxy(EnvVars envVars) {
super();
this.envVars = envVars;
}
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* <p>Description: 拦截所有目标类方法的调用</p>
* @param obj 目标实例对象
* @param method 目标方法的反射对象
* @param args 方法的参数
* @param proxy 代理类的实例
* @return
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String url = (String) args[0];
args[0] = this.checkUrl(url);// 重新包装URL
//代理类调用父类的方法
return proxy.invokeSuper(obj, args);
}
/**
* <p>Description: 判断当前请求的URL是否在需要代理跳转的URL里面</p>
* @param url 当前请求的URL
* @return
*/
private String checkUrl(String url) {
JSONObject urlJSONObject = null;
String proxyUrlArr = envVars.get("proxy_url");// 需要代理跳转的URL
if(StringUtils.isNotBlank(proxyUrlArr)) {
JSONArray proxyUrlJSONArray = JSONArray.fromObject(proxyUrlArr);
if(null != proxyUrlJSONArray) {
for (Object object : proxyUrlJSONArray.toArray()) {
String proxyUrl = (String) object;
// 如果当前请求的URL在需要代理跳转的URL里面,且代理URL不为空时,则使用代理URL
if(url.startsWith(proxyUrl)) {
String proxyServerHost = envVars.get("proxy_server_host");
if(StringUtils.isNotBlank(proxyServerHost)) {
urlJSONObject = new JSONObject();
urlJSONObject.put("url", url);
urlJSONObject.put("proxy_url", proxyServerHost);
}
break;
}
}
}
}
if(null != urlJSONObject) {
return urlJSONObject.toJSONString();
}
return url;
}
}
请求类:
import java.io.PrintStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import com.alibaba.fastjson.JSONObject;
import com.nd.sdp.portal.jenkins.factory.util.HttpsUtil;
public class HttpClientImpl {
/**
* <p>Description: 发送post请求 </p>
* @param url 请求URL
* @param requestBody 请求body
* @param isPrint 是否打印响应body
* @param printStream
*/
public String postForObject(String url, String requestBody, boolean isPrint, PrintStream printStream) {
boolean isProxy = false;// 是否需要代理
String proxyUrl = "";
String actualUrl = "";
try {
JSONObject urlJSONObject = (JSONObject) JSONObject.parse(url);
actualUrl = urlJSONObject.getString("url");// 真实URL
proxyUrl = urlJSONObject.getString("proxy_url");// 代理URL
isProxy = true;
} catch (Exception e) {
proxyUrl = url;
isProxy = false;
}
PostMethod postMethod = new PostMethod(proxyUrl);
printStream.println("[INFO] URL(POST):" + proxyUrl + " body:" + requestBody);
if(isProxy) {
postMethod.setRequestHeader("APF-HEADER-ACTUAL-URL", actualUrl);// 使用代理时,需要Header头上加参数
printStream.println("[INFO] setRequestHeader :" + actualUrl);
}
String responseBodyStr = "";
boolean flag = false;
//重试三次
for (int i = 0; i < 3; i++) {
if(flag){
break;
}
try {
postMethod.setRequestEntity(new StringRequestEntity(requestBody, "application/json", "utf-8"));
postMethod.addRequestHeader(new Header("Content-Type", "application/json;charset=utf-8"));
HttpClient client = new HttpClient();
client.getHttpConnectionManager().getParams().setSoTimeout(120 * 1000);
client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
int status = client.executeMethod(postMethod);
String prefix = status == 200 ? "[INFO] 发布成功" : "【ERROR】 发布失败";
printStream.println(prefix + " statusCode:" + status);
String response = postMethod.getResponseBodyAsString();
if(isPrint) {
printStream.println(prefix + " response body:" + response);
}
if (status!=200) {
throw new Exception("status " + status);
}
responseBodyStr = response;
flag = true;
} catch (Exception e) {
if(i==2){
String msg = "【ERROR】 发布失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
throw new RuntimeException(e);
} else {
String msg = "发布失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
}
} finally {
postMethod.releaseConnection();
}
}
return responseBodyStr;
}
/**
* <p>Description: 发送put请求 </p>
* @param url 请求URL
* @param requestBody 请求body
* @param isPrint 是否打印响应body
* @param printStream
*/
public String putForObject(String url, String requestBody, boolean isPrint, PrintStream printStream) {
boolean isProxy = false;// 是否需要代理
String proxyUrl = "";
String actualUrl = "";
try {
JSONObject urlJSONObject = (JSONObject) JSONObject.parse(url);
actualUrl = urlJSONObject.getString("url");// 真实URL
proxyUrl = urlJSONObject.getString("proxy_url");// 代理URL
isProxy = true;
} catch (Exception e) {
proxyUrl = url;
isProxy = false;
}
PutMethod putMethod = new PutMethod(proxyUrl);
printStream.println("[INFO] URL(PUT):" + proxyUrl + " body:" + requestBody);
if(isProxy) {
putMethod.setRequestHeader("APF-HEADER-ACTUAL-URL", actualUrl);// 使用代理时,需要Header头上加参数
printStream.println("[INFO] setRequestHeader :" + actualUrl);
}
String responseBodyStr = "";
boolean flag = false;
//重试三次
for (int i = 0; i < 3; i++) {
if(flag){
break;
}
try {
putMethod.setRequestEntity(new StringRequestEntity(requestBody, "application/json", "utf-8"));
putMethod.addRequestHeader(new Header("Content-Type", "application/json;charset=utf-8"));
HttpClient client = new HttpClient();
client.getHttpConnectionManager().getParams().setSoTimeout(120 * 1000);
client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
int status = client.executeMethod(putMethod);
String prefix = status == 200 ? "[INFO] 发布成功" : "【ERROR】 发布失败";
printStream.println(prefix + " statusCode:" + status);
String response = putMethod.getResponseBodyAsString();
if(isPrint) {
printStream.println(prefix + " response body:" + response);
}
if (status!=200) {
throw new Exception("status " + status);
}
responseBodyStr = response;
flag = true;
} catch (Exception e) {
if(i==2){
String msg = "【ERROR】 发布失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
throw new RuntimeException(e);
} else {
String msg = "发布失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
}
} finally {
putMethod.releaseConnection();
}
}
return responseBodyStr;
}
/**
* <p>Description: 发送get请求</p>
* @param url 请求URL
* @param printStream
* @return
*/
public String getForObject(String url, PrintStream printStream) {
boolean isProxy = false;// 是否需要代理
String proxyUrl = "";
String actualUrl = "";
try {
JSONObject urlJSONObject = (JSONObject) JSONObject.parse(url);
actualUrl = urlJSONObject.getString("url");// 真实URL
proxyUrl = urlJSONObject.getString("proxy_url");// 代理URL
isProxy = true;
} catch (Exception e) {
proxyUrl = url;
isProxy = false;
}
GetMethod getMethod = new GetMethod(proxyUrl);
printStream.println("[INFO] URL(GET): " + proxyUrl);
if(isProxy) {
getMethod.setRequestHeader("APF-HEADER-ACTUAL-URL", actualUrl);// 使用代理时,需要Header头上加参数
printStream.println("[INFO] setRequestHeader :" + actualUrl);
} else {
if(proxyUrl.startsWith("https://")) {// 无视Https证书是否正确
return HttpsUtil.getMethod(proxyUrl);
}
}
String responseBodyStr = "";
boolean flag = false;
//重试三次
for (int i = 0; i < 3; i++) {
if(flag){
break;
}
try {
HttpClient client = new HttpClient();
client.getHttpConnectionManager().getParams().setSoTimeout(120 * 1000);
client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
int status = client.executeMethod(getMethod);
String prefix = status == 200 ? "[INFO] 获取成功" : "【ERROR】 获取失败";
printStream.println(prefix + " statusCode:" + status);
if (status!=200) {
throw new Exception("status " + status);
}
byte[] responseBody = getMethod.getResponseBody();
responseBodyStr = new String(responseBody, "utf-8");
flag = true;
} catch (Exception e) {
if(i==2){
String msg = "【ERROR】获取失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
throw new RuntimeException(e);
} else {
String msg = "获取失败: " + e.getMessage() + ",第" + i + "次调用";
printStream.println(msg);
}
} finally {
getMethod.releaseConnection();
}
}
return responseBodyStr;
}
}
客户端调用方式:
HttpClientProxy proxy = new HttpClientProxy(envs);
HttpClientImpl httpClient = (HttpClientImpl) proxy.getProxy(HttpClientImpl.class);
// GET
String url = "";
String responeBody = httpClient.getForObject(url, listener.getLogger());
// POST
httpClient.postForObject(url, obj.toJSONString(), false, listener.getLogger());
URL配置信息:
{
"proxy_server_host":"https://xxx/proxy", // 代理服务URL
"proxy_url":[// 需要被代理的AWS外站地址
"https://xxx.aws.com/",
"https://xxx.awsca.com/"
]
}