微服务, 一个小玩意,降低开发运维测试团队的沟通成本
EurekaController.status 方法 , 返回了封装的各种应用信息, 通过 freemarker 模板展示到页面,
实际应用中为了运维方便,我们可能需要根据需求定制些小功能,例如:应用的上线、下线、删除等, 再例如将 swagger 地址维护进来等。
或者是,注册到 eureka 上的应用 ip 默认是内网的, 但是开发在使用 swagger 的时候取内网ip 就不能访问了,因此采取了配置 + js 转换的思路来实现, 可以做到不影响后端服务架构。以下是详述:
- 首先是切面:
import java.util.Map;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author zhouman
*
*/
@Aspect
@Component
public class EurekaControllerAspect {
@Value("${inner.out:\"\"}")
private String innerOutTmp;
/**
* 定义拦截规则:拦截所有 controller 里面所有被 注解的方法。 拦截器具体实现
*
* @param pjp
* @throws Throwable
*/
@Before("execution(* org.springframework.cloud.netflix.eureka.server.EurekaController.status(..))")
public void setProperties(JoinPoint point) throws Throwable {
Map<String, Object> model = (Map<String, Object>) point.getArgs()[1];
JSONObject json = parse();
model.put("ipTransfer", json);
}
private JSONObject parse() throws JSONException {
JSONObject tmp = new JSONObject();
String[] split = innerOutTmp.split(",");
if(split.length > 0) {
for (int i = 0; i < split.length; i++) {
String single = split[i];
parseSingle(single, tmp);
}
}
return tmp;
}
private JSONObject parseSingle(String single, JSONObject json) throws JSONException {
String[] split = single.split(":");
if(split.length > 0) {
json.put(split[0], split[1]);
return json;
}
return null;
}
}
- 然后是 status.ftl 文件
(从 eureka 包里面原样拷出来后,做了点修改,主要看 replaceOutIp 函数)
<#import "/spring.ftl" as spring />
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<base href="<@spring.url basePath/>">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Eureka</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="eureka/css/wro.css">
</head>
<body id="one">
<#include "header.ftl">
<div class="container-fluid xd-container">
<#include "navbar.ftl">
<h1>Instances currently registered with Eureka</h1>
<table id='instances' class="table table-striped table-hover">
<thead>
<tr><th>Application</th><th>AMIs</th><th>Availability Zones</th><th>Status</th></tr>
</thead>
<tbody>
<#if apps?has_content>
<#list apps as app>
<tr>
<td><b>${app.name}</b></td>
<td>
<#list app.amiCounts as amiCount>
<b>${amiCount.key}</b> (${amiCount.value})<#if amiCount_has_next>,</#if>
</#list>
</td>
<td>
<#list app.zoneCounts as zoneCount>
<b>${zoneCount.key}</b> (${zoneCount.value})<#if zoneCount_has_next>,</#if>
</#list>
</td>
<td>
<#list app.instanceInfos as instanceInfo>
<#if instanceInfo.isNotUp>
<font color=red size=+1><b>
</#if>
<b>status: ${instanceInfo.status} counts: (${instanceInfo.instances?size})</b>
<#if instanceInfo.isNotUp>
</b></font>
</#if>
<#list instanceInfo.instances as instance>
<div>
<#if instance.isHref>
<a class="forswagger" href="${instance.url}" target="_blank">${instance.id}</a>
<#else>
${instance.id}
</#if>
| operate:
<span>
<#if instanceInfo.isNotUp>
<#else>
<a href="javascript:void(0);" οnclick="doDown('${app.name}', '${instance.id}', this.parentNode.previousElementSibling);">下线</a>
</#if>
</span>
<span><a href="javascript:void(0);" οnclick="doDelete('${app.name}', '${instance.id}', this.parentNode.parentNode);">删除</a></span>
</div>
</#list>
</#list>
</td>
</tr>
</#list>
<#else>
<tr><td colspan="4">No instances available</td></tr>
</#if>
</tbody>
</table>
<h1>General Info</h1>
<table id='generalInfo' class="table table-striped table-hover">
<thead>
<tr><th>Name</th><th>Value</th></tr>
</thead>
<tbody>
<#list statusInfo.generalStats?keys as stat>
<tr>
<td>${stat}</td><td>${statusInfo.generalStats[stat]!""}</td>
</tr>
</#list>
<#list statusInfo.applicationStats?keys as stat>
<tr>
<td>${stat}</td><td>${statusInfo.applicationStats[stat]!""}</td>
</tr>
</#list>
</tbody>
</table>
<h1>Instance Info</h1>
<table id='instanceInfo' class="table table-striped table-hover">
<thead>
<tr><th>Name</th><th>Value</th></tr>
<thead>
<tbody>
<#list instanceInfo?keys as key>
<tr>
<td>${key}</td><td>${instanceInfo[key]!""}</td>
</tr>
</#list>
</tbody>
</table>
</div>
<script type="text/javascript" src="eureka/js/wro.js" ></script>
<script type="text/javascript">
$(document).ready(function() {
$('table.stripeable tr:odd').addClass('odd');
$('table.stripeable tr:even').addClass('even');
});
function doDown(appName, instanceId, aimObjToChangeStyle, callback) {
var url = "/eureka/apps/" + appName + "/" + instanceId + "/status?value=OUT_OF_SERVICE";
if(!callback) {
callback = function() {
window.location.reload();
//aimObjToChangeStyle.style.borderBottom = "red 2px solid";
}
}
ajaxMethod(url, "PUT", callback);
}
function doDelete(appName, instanceId, aimObjToRemove) {
doDown(appName, instanceId, aimObjToRemove, function() {
var url = "/eureka/apps/" + appName + "/" + instanceId;
ajaxMethod(url, "DELETE", function() {
//aimObjToRemove.remove();
window.location.reload();
});
});
}
function ajaxMethod(url, method, callback) {
var httpRequest = new XMLHttpRequest();//第一步:建立所需的对象
httpRequest.open(method, url, true);//第二步:打开连接 将请求参数写在url中 ps:"./Ptest.php?name=test&nameone=testone"
httpRequest.send();//第三步:发送请求 将请求参数写在URL中
/**
* 获取数据后的处理程序
*/
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var json = httpRequest.responseText;//获取到json字符串,还需解析
console.log(json);
if(callback) {
callback();
}
}
};
}
function replaceOutIp() {
var ipTmp = '${ipTransfer}';
console.log("ip config: " + ipTmp);
var ipJson = eval("(" + ipTmp + ")");
var divDoms = document.getElementsByClassName("forswagger");
for (var i = 0; i < divDoms.length; i++) {
var dDom = divDoms[i];
parseDom(dDom, ipJson);
}
}
function parseDom(dDom, ipJson) {
for(var k in ipJson) {
if(dDom.href.indexOf(k) != -1) {
dDom.href = dDom.href.replace(k, ipJson[k]);
console.log("ip 替换为外网ip: " + dDom.href);
return;
}
}
console.error("status.ftl 未配置内网 ip 对应的外网ip, swagger可能无法访问 ,href: " + dDom.href);
}
replaceOutIp();
</script>
</body>
</html>
- 再加个配置到配置文件:
inner.out=内网ip:外网ip,内网ip:外网ip
- 最后加上扫描路径:
@SpringBootApplication(scanBasePackages = {
"org.xlc.eureka.aspect"
})