springcloudベースのサービス階調パブリケーション(二)
14537 ワード
グレースケールリンクテスト
文書ディレクトリ
1テストインタフェース実装
すべてのサービスがグレーjarにアクセスした後、リンク効果をテストするのは便利ではありません.したがって、すべてのサービスアクセス依存性の後、リンクテストインタフェース側を予め設定して、全体のリンク効果が予想されるかどうかをテストする.
/**
*
*
* @author xie
*/
@RestController
@RequestMapping("/gray/test")
public class GrayRouteTestController {
private Logger logger = LoggerFactory.getLogger(GrayRouteTestController.class);
@Resource
private EurekaDiscoveryClient discoveryClient;
@Resource()
private ApplicationInfoManager applicationInfoManager;
@Resource
private RestTemplate restTemplate;
/**
* @param nodes
* @return
*/
@RequestMapping(value = "linksTest", method = RequestMethod.POST)
public TestResult nodeTest(@RequestBody List nodes) {
InstanceInfo startNodeServer = applicationInfoManager.getInfo();
String currentServiceId = startNodeServer.getAppName().toLowerCase();
if (!nodes.get(0).equals(currentServiceId)) {
nodes.add(0,currentServiceId);
}
//
RequestNode requestNode = convert2RequestNode(nodes);
List resultNodes = internalNodeTest(requestNode);
ResultNode startNode = new ResultNode();
startNode.setServiceId(startNodeServer.getAppName().toLowerCase());
startNode.setHost(startNodeServer.getHostName());
startNode.setPort(startNodeServer.getPort());
String serverStatus = startNodeServer.getMetadata().get(Constant.INSTANCE_STATUS);
startNode.setInstanceStatus(serverStatus);
resultNodes.add(startNode);
Comparator tComparator = Collections.reverseOrder();
resultNodes.sort(tComparator);
TestResult testResult = new TestResult();
ResultNode resultNode = resultNodes.get(resultNodes.size() - 1);
testResult.setSuccess(resultNode.isSuccess());
testResult.setMessage(resultNode.getMessage());
testResult.setNodeList(resultNodes);
testResult.setTotal(nodes.size());
testResult.setRoute2Gray(GrayUtils.isGray());
testResult.setFailureCount(resultNode.isSuccess() ? 0 : nodes.size() - (resultNodes.size() - 1));
return testResult;
}
@RequestMapping(value = "internalNodeTest", method = RequestMethod.POST)
public List internalNodeTest(@RequestBody RequestNode requestNode) {
//
RequestNode next = requestNode.getNext();
if (next == null) {
logger.info("request is end");
List list = new ArrayList<>();
return list;
}
GrayUtils.currentSelectServer.set(new Server(next.getServiceId()));
try {
ResultNode nextNodeServer = new ResultNode();
nextNodeServer.setIndex(next.getIndex());
nextNodeServer.setServiceId(next.getServiceId());
List resultNodes = new ArrayList<>();
// url
String serverUrl = getServerUrl(next.getServiceId());
if (StringUtils.isEmpty(serverUrl)) {
nextNodeServer.setMessage(" " + next.getServiceId() + " ");
nextNodeServer.setSuccess(false);
resultNodes.add(nextNodeServer);
return resultNodes;
}
HttpHeaders requestHeaders = new HttpHeaders();
//
requestHeaders.add(Constant.ROUTE_TO_GRAY, String.valueOf(GrayUtils.isGray()));
HttpEntity requestEntity = new HttpEntity<>(next, requestHeaders);
try {
ResponseEntity response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
// ,
resultNodes = JSON.parseArray(response.getBody(), ResultNode.class);
Server testServer = GrayUtils.getTestServer();
nextNodeServer.setHost(testServer.getHost());
nextNodeServer.setPort(testServer.getPort());
if (testServer instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer enabledServer = (DiscoveryEnabledServer) testServer;
InstanceInfo instanceInfo = enabledServer.getInstanceInfo();
String serverStatus = instanceInfo.getMetadata().get(Constant.INSTANCE_STATUS);
nextNodeServer.setInstanceStatus(serverStatus);
nextNodeServer.setIndex(next.getIndex());
}
} catch (Exception ex) {
//
logger.warn("links test failure:{}", ex.getMessage());
nextNodeServer.setMessage("[route_to_gray:" + GrayUtils.isGray() + "]" + ex.getMessage() + serverUrl);
nextNodeServer.setSuccess(false);
resultNodes.add(nextNodeServer);
}
resultNodes.add(nextNodeServer);
return resultNodes;
} finally {
GrayUtils.removeTestServer();
}
}
private RequestNode convert2RequestNode(List nodes) {
RequestNode nextNode = null;
for (int i = nodes.size() - 1; i >= 0; i--) {
if (nextNode == null) {
nextNode = new RequestNode(nodes.get(i), i);
} else {
RequestNode tNextNode = new RequestNode(nodes.get(i), i);
tNextNode.setNext(nextNode);
nextNode = tNextNode;
}
}
return nextNode;
}
/**
* url
* @param nextNodeId
* @return
*/
private String getServerUrl(String nextNodeId) {
List instances = discoveryClient.getInstances(nextNodeId);
if (instances.isEmpty()) {
return null;
}
ServiceInstance serviceInstance = instances.get(0);
String contextPath = serviceInstance.getMetadata().get(CONTEXT_PATH);
String temContextPath = contextPath == null ? "/" : contextPath;
String url = "http://" + nextNodeId.toLowerCase() + temContextPath + "/gray/test/internalNodeTest";
return url;
}
}
2測定インタフェースを迂回してブロックする
通常、サービスインタフェースへのアクセスにはログインが必要であり、ログインしていないインタフェースはブロック結果が実際にあり、通常、アクセスブロックの実装はfilter実装するか、HandlerInterceptor(mvcプロジェクト)でテストインタフェースがブロックされないようにするには、これらのブロックを迂回する方法が必要である.ビジネスコードを変更しない場合、ブロックを迂回して、エージェントはよく使われる方法です.結果springエージェントの特徴は、簡単に実現できます.
2.1 filterまたはHandlerInterceptorエージェントの作成
public class InterceptorBeanProcessor implements BeanPostProcessor {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private static final Map baseTypes = new HashMap<>();
private Enhancer enhancer = new Enhancer();
private boolean filterClassExist;
private boolean interceptorClassExist;
private GrayProperties grayProperties;
public InterceptorBeanProcessor(GrayProperties grayProperties){
this.grayProperties = grayProperties;
}
static {
baseTypes.put("int", 0);
baseTypes.put("short", 0);
baseTypes.put("long", 0L);
baseTypes.put("double", 0d);
baseTypes.put("float", 0f);
baseTypes.put("boolean", false);
baseTypes.put("char", (char) 0);
baseTypes.put("byte", Byte.valueOf((byte) 0));
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// filter
Object filterProxy = createFilterProxy(bean);
if(bean.equals(filterProxy)){
// Interceptor
return createInterceptorProxy(bean);
}else {
return filterProxy;
}
}
private Object createFilterProxy(Object bean){
if(!filterClassExist()) {
return bean;
}
if (bean instanceof Filter) {
Class> clazz = bean.getClass();
try {
return cglibProxy(clazz, new FilterMethodInterceptor(bean,grayProperties));
} catch (Throwable e) {
logger.warn("create {} filter proxy failure :{}", clazz.getName(), e.getMessage());
return bean;
}
}
return bean;
}
private Object createInterceptorProxy(Object bean){
if(!interceptorClassExist()) {
return bean;
}
if (bean instanceof HandlerInterceptor) {
Class> clazz = bean.getClass();
try {
return cglibProxy(clazz, new HandlerInterceptorMethodInterceptor(bean,grayProperties));
} catch (Throwable e) {
logger.warn("create {} interceptor proxy failure:{}", clazz.getName(), e.getMessage());
return bean;
}
}
return bean;
}
/**
* servlet ,filter
* @return
*/
private boolean filterClassExist() {
if(filterClassExist){
return true;
}
try {
Class clzz = Filter.class;
filterClassExist = true;
}catch (Throwable e){
filterClassExist = false;
}
return false;
}
private boolean interceptorClassExist() {
if(interceptorClassExist){
return true;
}
try {
Class clzz = HandlerInterceptor.class;
interceptorClassExist = true;
}catch ( Throwable e){
interceptorClassExist = false;
}
return false;
}
/**
* final (cglib , final )
*
* @param clazz
* @return
*/
private Object cglibProxy(Class clazz, Callback callback) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(callback);
Constructor>[] constructors = clazz.getConstructors();
Parameter[] parameters = constructors[0].getParameters();
Object[] arguments = new Object[parameters.length];
Class[] paramsTypes = new Class[parameters.length];
for (int i = 0; i < arguments.length; i++) {
Class> type = parameters[i].getType();
arguments[i] = baseTypes.get(type.getSimpleName().toLowerCase());
paramsTypes[i] = type;
}
return enhancer.create(paramsTypes, arguments);
}
}
2.2ブロックをスキップするかどうかの処理
public class InterceptorAdapter implements Callback {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final String FILTER_METHOD="doFilter";
private final String INTERCEPTOR_METHOD="preHandle";
PathMatcher pathMatcher = new AntPathMatcher();
private Object target;
private GrayProperties grayProperties;
public InterceptorAdapter(Object target, GrayProperties grayProperties) {
this.target = target;
this.grayProperties = grayProperties;
}
/**
* , filter
* @param proxy
* @param method
* @param args
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IOException
* @throws ServletException
*/
protected Object doFilter(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException, IOException, ServletException {
// doFilter
if (FILTER_METHOD.equals(method.getName())) {
HttpServletRequest servletRequest = (HttpServletRequest) args[0];
ServletResponse servletResponse = (ServletResponse) args[1];
FilterChain filterChain = (FilterChain) args[2];
String servletPath = servletRequest.getServletPath();
// , filter
if (skipCurrentPath(servletPath)) {
logger.debug("[{}] is gray test path ,skip filter of {}",servletPath,target.getClass().getSimpleName());
filterChain.doFilter(servletRequest, servletResponse);
} else {
method.invoke(target, args);
}
} else {
method.setAccessible(true);
return method.invoke(target, args);
}
return null;
}
private boolean skipCurrentPath(String servletPath){
Set skipPaths = grayProperties.getSkipPaths();
for(String pattern :skipPaths){
if(pathMatcher.match(pattern,servletPath)){
return true;
}
}
return false;
}
/**
* , interceptor
* @param proxy
* @param method
* @param args
* @return
* @throws Exception
*/
protected Object preHandle(Object proxy, Method method, Object[] args)
throws Exception {
// preHandle
if (INTERCEPTOR_METHOD.equals(method.getName())) {
HttpServletRequest servletRequest = (HttpServletRequest) args[0];
String servletPath = servletRequest.getServletPath();
// ,
if (skipCurrentPath(servletPath)) {
logger.debug("[{}] is gray test path ,skip interceptor of {}", servletPath, target.getClass().getSimpleName());
return true;
} else {
return method.invoke(target, args);
}
} else {
method.setAccessible(true);
return method.invoke(target, args);
}
}
/**
* ,
* @param proxy
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return doFilter(proxy,method,args);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return doFilter(proxy,method,args);
}
}