diff --git a/sermant-backend-lite/deployment/deployment.yaml b/sermant-backend-lite/deployment/deployment.yaml new file mode 100644 index 0000000000..1aa119d4d4 --- /dev/null +++ b/sermant-backend-lite/deployment/deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: v1 +kind: Service +metadata: + name: sermant-backend-lite-service + labels: + app: sermant-backend-lite +spec: + type: NodePort + ports: + - port: 8900 + nodePort: 30001 + name: sermant-backend-lite-service-http + - port: 6888 + nodePort: 30002 + name: sermant-backend-lite-service-netty + selector: + app: sermant-backend-lite +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sermant-backend-lite-deployment + labels: + app: sermant-backend-lite +spec: + replicas: 1 + selector: + matchLabels: + app: sermant-backend-lite + template: + metadata: + labels: + app: sermant-backend-lite + spec: + containers: + - name: sermant-backend-lite-deployment + image: { image.address } + imagePullPolicy: Always + resources: + requests: + memory: "4096Mi" + cpu: "2" + limits: + memory: "4096Mi" + cpu: "2" + ports: + - containerPort: 8900 + - containerPort: 6888 + imagePullSecrets: + - name: default-secret \ No newline at end of file diff --git a/sermant-backend-lite/image/Dockerfile b/sermant-backend-lite/image/Dockerfile new file mode 100644 index 0000000000..9b195509ea --- /dev/null +++ b/sermant-backend-lite/image/Dockerfile @@ -0,0 +1,10 @@ +FROM java:8 + +WORKDIR /home + +COPY sermant-backend-lite.jar /home +COPY start.sh /home + +RUN chmod -R 777 /home + +ENTRYPOINT ["sh", "/home/start.sh"] \ No newline at end of file diff --git a/sermant-backend-lite/image/start.sh b/sermant-backend-lite/image/start.sh new file mode 100644 index 0000000000..d0ce0f9c66 --- /dev/null +++ b/sermant-backend-lite/image/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +java -jar /home/sermant-backend-lite.jar \ No newline at end of file diff --git a/sermant-backend-lite/pom.xml b/sermant-backend-lite/pom.xml index 59a3bcd4d5..f7ca2e5407 100644 --- a/sermant-backend-lite/pom.xml +++ b/sermant-backend-lite/pom.xml @@ -64,6 +64,7 @@ + ${project.artifactId} org.xolstice.maven.plugins diff --git a/sermant-backend-lite/src/main/webapp/sermant-frontend/src/assets/logo.svg b/sermant-backend-lite/src/main/webapp/sermant-frontend/src/assets/logo.svg index bc826fed80..af4f5f59b5 100644 --- a/sermant-backend-lite/src/main/webapp/sermant-frontend/src/assets/logo.svg +++ b/sermant-backend-lite/src/main/webapp/sermant-frontend/src/assets/logo.svg @@ -1 +1,12 @@ - \ No newline at end of file + + + Layer 1 + + + + + + + + + \ No newline at end of file diff --git a/sermant-plugins/sermant-router/config/config.yaml b/sermant-plugins/sermant-router/config/config.yaml index 02acd60103..df7dd616d4 100644 --- a/sermant-plugins/sermant-router/config/config.yaml +++ b/sermant-plugins/sermant-router/config/config.yaml @@ -8,4 +8,6 @@ router.plugin: # 是否使用请求信息做路由 use-request-router: false # 使用请求信息做路由时的tags - request-tags: [] \ No newline at end of file + request-tags: [] + # 需要解析的请求头的tag + parse-header-tag: '' \ No newline at end of file diff --git a/sermant-plugins/sermant-router/dubbo-router-service/src/main/java/com/huaweicloud/sermant/router/dubbo/service/AbstractDirectoryServiceImpl.java b/sermant-plugins/sermant-router/dubbo-router-service/src/main/java/com/huaweicloud/sermant/router/dubbo/service/AbstractDirectoryServiceImpl.java index e8467e0e98..17e88c6d12 100644 --- a/sermant-plugins/sermant-router/dubbo-router-service/src/main/java/com/huaweicloud/sermant/router/dubbo/service/AbstractDirectoryServiceImpl.java +++ b/sermant-plugins/sermant-router/dubbo-router-service/src/main/java/com/huaweicloud/sermant/router/dubbo/service/AbstractDirectoryServiceImpl.java @@ -22,6 +22,7 @@ import com.huaweicloud.sermant.router.common.constants.RouterConstant; import com.huaweicloud.sermant.router.common.request.RequestHeader; import com.huaweicloud.sermant.router.common.utils.CollectionUtils; +import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; import com.huaweicloud.sermant.router.config.cache.ConfigCache; import com.huaweicloud.sermant.router.config.entity.EnabledStrategy; @@ -122,6 +123,17 @@ public Object selectInvokers(Object registryDirectory, Object[] arguments, Objec return getZoneInvokers(targetService, targetInvokers); } + /** + * 解析下dubbo的附件信息 + * + * @param invocation dubbo的invocation + * @return {@link Map}<{@link String}, {@link Object}> + */ + private Map parseAttachments(Object invocation) { + Map attachments = DubboReflectUtils.getAttachments(invocation); + return FlowContextUtils.decodeAttachments(attachments); + } + /** * 获取dubbo应用 group * @@ -179,7 +191,7 @@ private List getTargetInvokersByRules(List invokers, Object invo List rules = RuleUtils .getRules(configuration, targetService, interfaceName, DubboCache.INSTANCE.getAppName()); List routes = RouteUtils.getRoutes(rules, DubboReflectUtils.getArguments(invocation), - DubboReflectUtils.getAttachments(invocation)); + parseAttachments(invocation)); if (!CollectionUtils.isEmpty(routes)) { return RuleStrategyHandler.INSTANCE.getMatchInvokers(targetService, invokers, routes); } else { @@ -189,7 +201,7 @@ private List getTargetInvokersByRules(List invokers, Object invo } private List getTargetInvokersByRequest(String targetName, List invokers, Object invocation) { - Map attachments = DubboReflectUtils.getAttachments(invocation); + Map attachments = parseAttachments(invocation); List requestTags = routerConfig.getRequestTags(); if (CollectionUtils.isEmpty(requestTags)) { return invokers; diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/config/RouterConfig.java b/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/config/RouterConfig.java index 391910b983..98155e0e70 100644 --- a/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/config/RouterConfig.java +++ b/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/config/RouterConfig.java @@ -81,6 +81,12 @@ public class RouterConfig implements PluginConfig { */ private Map parameters; + /** + * 需要解析的请求头的tag + */ + @ConfigFieldKey("parse-header-tag") + private String parseHeaderTag; + /** * 构造方法 */ @@ -167,4 +173,12 @@ public Map getParameters() { public void setParameters(Map parameters) { this.parameters = parameters; } + + public String getParseHeaderTag() { + return parseHeaderTag; + } + + public void setParseHeaderTag(String parseHeaderTag) { + this.parseHeaderTag = parseHeaderTag; + } } \ No newline at end of file diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/utils/FlowContextUtils.java b/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/utils/FlowContextUtils.java index 83cbd4f8e6..a096a801de 100644 --- a/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/utils/FlowContextUtils.java +++ b/sermant-plugins/sermant-router/router-common/src/main/java/com/huaweicloud/sermant/router/common/utils/FlowContextUtils.java @@ -16,7 +16,9 @@ package com.huaweicloud.sermant.router.common.utils; +import com.huaweicloud.sermant.core.plugin.config.PluginConfigManager; import com.huaweicloud.sermant.core.utils.StringUtils; +import com.huaweicloud.sermant.router.common.config.RouterConfig; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -24,6 +26,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; /** @@ -35,12 +38,13 @@ public class FlowContextUtils { private static final Base64.Decoder DECODER = Base64.getDecoder(); private static final int TAG_PART_NUM = 2; + private static volatile RouterConfig routerConfig; private FlowContextUtils() { } /** - * sw8-correlation流量标识解码. + * 解析base64加密信息成明文信息 * * @param encodeTagsString encodeTagsString * @return 解密后的流量标识 @@ -58,8 +62,62 @@ public static Map> decodeTags(String encodeTagsString) { } List list = new ArrayList<>(); list.add(new String(DECODER.decode(parts[1]), StandardCharsets.UTF_8)); - tagMapping.put(new String(DECODER.decode(parts[0]), StandardCharsets.UTF_8), list); + tagMapping.put(decode(parts[0]).toLowerCase(Locale.ENGLISH), list); } return tagMapping; } + + /** + * 解码附件 + * + * @param attachments 附件信息 + * @return {@link Map}<{@link String}, {@link Object}> + */ + public static Map decodeAttachments(Map attachments) { + if (attachments == null || attachments.isEmpty() || StringUtils.isBlank(getTagName())) { + return attachments; + } + Object encode = attachments.get(getTagName()); + if (encode == null) { + return attachments; + } + String encodeTags = String.valueOf(encode); + Map allAttachments = new HashMap<>(attachments); + String[] tags = encodeTags.split(","); + for (String tag : tags) { + final String[] parts = tag.split(":"); + if (parts.length != TAG_PART_NUM) { + continue; + } + allAttachments.put(decode(parts[0]).toLowerCase(Locale.ENGLISH), decode(parts[1])); + } + return Collections.unmodifiableMap(allAttachments); + } + + /** + * 解码 + * + * @param encodeString 编码的字符串 + * @return {@link String} + */ + private static String decode(String encodeString) { + return new String(DECODER.decode(encodeString), StandardCharsets.UTF_8); + } + + /** + * 获取请求头中需要解析的tag + * + * @return {@link String} + */ + public static String getTagName() { + if (routerConfig == null) { + synchronized (FlowContextUtils.class) { + if (routerConfig == null) { + routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); + return routerConfig.getParseHeaderTag(); + } + } + } + return routerConfig.getParseHeaderTag(); + } } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/HttpUrlConnectionConnectDeclarer.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/HttpUrlConnectionConnectDeclarer.java new file mode 100644 index 0000000000..7774b35268 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/HttpUrlConnectionConnectDeclarer.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.declarer; + +import com.huaweicloud.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.declarer.InterceptDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.matcher.ClassMatcher; +import com.huaweicloud.sermant.core.plugin.agent.matcher.MethodMatcher; + +/** + * 定义JDK 1.8版本的java.net.HttpURLConnection的拦截点信息
+ * + * @author yuzl 俞真龙 + * @since 2022-10-25 + */ +public class HttpUrlConnectionConnectDeclarer extends AbstractPluginDeclarer { + private static final String METHOD_NAME = "connect"; + + private static final String INTERCEPTOR_CLASS = + "com.huaweicloud.sermant.router.spring.interceptor.HttpUrlConnectionConnectInterceptor"; + + @Override + public ClassMatcher getClassMatcher() { + return ClassMatcher.isExtendedFrom("java.net.HttpURLConnection"); + } + + @Override + public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) { + return new InterceptDeclarer[] { + InterceptDeclarer.build(MethodMatcher.nameEquals(METHOD_NAME), INTERCEPTOR_CLASS)}; + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/RestTemplateDeclarer.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/RestTemplateDeclarer.java new file mode 100644 index 0000000000..00ddf3317f --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/RestTemplateDeclarer.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.declarer; + +import com.huaweicloud.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.declarer.InterceptDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.matcher.ClassMatcher; +import com.huaweicloud.sermant.core.plugin.agent.matcher.MethodMatcher; + +/** + * org.springframework.web.client.RestTemplate的拦截点定义
+ * + * @author yuzl 俞真龙 + * @since 2022-10-27 + */ +public class RestTemplateDeclarer extends AbstractPluginDeclarer { + private static final String ENHANCE_CLASS = "org.springframework.web.client.RestTemplate"; + private static final String ENHANCE_METHOD = "doExecute"; + private static final String INTERCEPTOR_CLASS = + "com.huaweicloud.sermant.router.spring.interceptor.RestTemplateInterceptor"; + + @Override + public ClassMatcher getClassMatcher() { + return ClassMatcher.nameEquals(ENHANCE_CLASS); + } + + @Override + public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) { + return new InterceptDeclarer[] { + InterceptDeclarer.build(MethodMatcher.nameEquals(ENHANCE_METHOD), INTERCEPTOR_CLASS)}; + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/SpringBootLoadClassDeclarer.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/SpringBootLoadClassDeclarer.java new file mode 100644 index 0000000000..bfe35d0b34 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/declarer/SpringBootLoadClassDeclarer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.declarer; + +import com.huaweicloud.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.declarer.InterceptDeclarer; +import com.huaweicloud.sermant.core.plugin.agent.matcher.ClassMatcher; +import com.huaweicloud.sermant.core.plugin.agent.matcher.MethodMatcher; +import com.huaweicloud.sermant.router.spring.interceptor.SpringBootLoadClassInterceptor; + +/** + * 增强SpringBootApplication类的main方法,用于加载一些特殊的类 + * + * @author yuzl 俞真龙 + * @since 2022-10-28 + */ +public class SpringBootLoadClassDeclarer extends AbstractPluginDeclarer { + private static final String[] ENHANCE_CLASS = {"org.springframework.boot.autoconfigure.SpringBootApplication"}; + + private static final String INTERCEPT_CLASS = SpringBootLoadClassInterceptor.class.getCanonicalName(); + + private static final String METHOD_NAME = "main"; + + @Override + public ClassMatcher getClassMatcher() { + return ClassMatcher.isAnnotatedWith(ENHANCE_CLASS); + } + + @Override + public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) { + return new InterceptDeclarer[]{ + InterceptDeclarer.build(MethodMatcher.nameEquals(METHOD_NAME).and(MethodMatcher.isStaticMethod()), + INTERCEPT_CLASS) + }; + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptor.java index 9d06cd6832..f7c785ceb6 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptor.java @@ -19,8 +19,10 @@ import com.huaweicloud.sermant.core.common.LoggerFactory; import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import com.huaweicloud.sermant.core.utils.StringUtils; import com.huaweicloud.sermant.router.common.request.RequestData; import com.huaweicloud.sermant.router.common.request.RequestHeader; +import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; import com.huaweicloud.sermant.router.common.utils.ReflectUtils; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; @@ -62,7 +64,7 @@ public ExecuteContext before(ExecuteContext context) { Request request = (Request) argument; Map> headers = getHeaders(request.headers()); setHeaders(request, headers); - ThreadLocalUtils.setRequestData(new RequestData(headers, getPath(request.url()), request.method())); + ThreadLocalUtils.setRequestData(new RequestData(decodeTags(headers), getPath(request.url()), request.method())); } return context; } @@ -142,4 +144,21 @@ private Optional getRequestHeader() { } return Optional.empty(); } + + private Map> decodeTags(Map> headers) { + if (StringUtils.isBlank(FlowContextUtils.getTagName())) { + return headers; + } + Map> newHeaders = new HashMap<>(headers); + if (headers != null && headers.size() > 0) { + List list = headers.get(FlowContextUtils.getTagName()); + if (list != null && list.size() > 0) { + String tagStr = list.get(0); + Map> stringListMap = FlowContextUtils.decodeTags(tagStr); + newHeaders.putAll(stringListMap); + return Collections.unmodifiableMap(newHeaders); + } + } + return headers; + } } \ No newline at end of file diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpClient4xInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpClient4xInterceptor.java index 1d99764da1..d291a28c3e 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpClient4xInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpClient4xInterceptor.java @@ -18,6 +18,7 @@ import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import com.huaweicloud.sermant.core.utils.StringUtils; import com.huaweicloud.sermant.router.common.request.RequestData; import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; @@ -56,7 +57,10 @@ public ExecuteContext before(ExecuteContext context) throws Exception { return context; } URI uri = optionalUri.get(); - Header[] headers = httpRequest.getHeaders("sw8-correlation"); + if (StringUtils.isBlank(FlowContextUtils.getTagName())) { + return context; + } + Header[] headers = httpRequest.getHeaders(FlowContextUtils.getTagName()); Map> flowTags = new HashMap<>(); if (headers != null && headers.length > 0) { for (Header header : headers) { diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java new file mode 100644 index 0000000000..11ce914c56 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.interceptor; + +import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; +import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import com.huaweicloud.sermant.core.utils.StringUtils; +import com.huaweicloud.sermant.router.common.request.RequestData; +import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; +import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 针对JDK 1.8版本的java.net.HttpURLConnection的一个增强拦截器
+ * + * @author yuzl 俞真龙 + * @since 2022-10-25 + */ +public class HttpUrlConnectionConnectInterceptor extends AbstractInterceptor { + @Override + public ExecuteContext before(ExecuteContext context) { + if (context.getObject() instanceof HttpURLConnection) { + HttpURLConnection connection = (HttpURLConnection) context.getObject(); + if (StringUtils.isBlank(FlowContextUtils.getTagName())) { + return context; + } + String encodeTag = connection.getRequestProperty(FlowContextUtils.getTagName()); + if (StringUtils.isBlank(encodeTag)) { + return context; + } + Map> tags = FlowContextUtils.decodeTags(encodeTag); + if (!tags.isEmpty()) { + RequestData requestData = new RequestData(tags, getPath(connection), connection.getRequestMethod()); + ThreadLocalUtils.setRequestData(requestData); + } + } + return context; + } + + private String getPath(HttpURLConnection connection) { + return Optional.ofNullable(connection.getURL()).map(URL::getPath).orElse("/"); + } + + @Override + public ExecuteContext after(ExecuteContext context) { + ThreadLocalUtils.removeRequestData(); + return context; + } + + @Override + public ExecuteContext onThrow(ExecuteContext context) throws Exception { + ThreadLocalUtils.removeRequestData(); + return super.onThrow(context); + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java index 5d788179c6..45a4a94564 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java @@ -19,6 +19,7 @@ import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; import com.huaweicloud.sermant.core.utils.ReflectUtils; +import com.huaweicloud.sermant.core.utils.StringUtils; import com.huaweicloud.sermant.router.common.request.RequestData; import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; @@ -50,13 +51,13 @@ public class OkHttp3ClientInterceptor extends AbstractInterceptor { @Override public ExecuteContext before(ExecuteContext context) throws Exception { final Optional rawRequest = getRequest(context); - if (!rawRequest.isPresent()) { + if (!rawRequest.isPresent() || StringUtils.isBlank(FlowContextUtils.getTagName())) { return context; } Request request = rawRequest.get(); URI uri = request.url().uri(); Headers headers = request.headers(); - String str = headers.get("sw8-correlation"); + String str = headers.get(FlowContextUtils.getTagName()); Map> decodeTags = FlowContextUtils.decodeTags(str); if (decodeTags.size() > 0) { ThreadLocalUtils.setRequestData(new RequestData(decodeTags, uri.getPath(), request.method())); diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttpClientInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttpClientInterceptor.java index 4867057555..61e161890b 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttpClientInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/OkHttpClientInterceptor.java @@ -19,6 +19,7 @@ import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; import com.huaweicloud.sermant.core.utils.ReflectUtils; +import com.huaweicloud.sermant.core.utils.StringUtils; import com.huaweicloud.sermant.router.common.request.RequestData; import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; @@ -50,13 +51,13 @@ public class OkHttpClientInterceptor extends AbstractInterceptor { @Override public ExecuteContext before(ExecuteContext context) throws Exception { final Optional rawRequest = getRequest(context); - if (!rawRequest.isPresent()) { + if (!rawRequest.isPresent() || StringUtils.isBlank(FlowContextUtils.getTagName())) { return context; } Request request = rawRequest.get(); URI uri = request.uri(); Headers headers = request.headers(); - String str = headers.get("sw8-correlation"); + String str = headers.get(FlowContextUtils.getTagName()); Map> decodeTags = FlowContextUtils.decodeTags(str); if (decodeTags.size() > 0) { ThreadLocalUtils.setRequestData(new RequestData(decodeTags, uri.getPath(), request.method())); diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/RestTemplateInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/RestTemplateInterceptor.java new file mode 100644 index 0000000000..28a4d85fac --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/RestTemplateInterceptor.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.interceptor; + +import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; +import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import com.huaweicloud.sermant.core.utils.StringUtils; +import com.huaweicloud.sermant.router.common.request.RequestData; +import com.huaweicloud.sermant.router.common.utils.FlowContextUtils; +import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; +import com.huaweicloud.sermant.router.spring.wrapper.RequestCallbackWrapper; + +import org.springframework.http.HttpMethod; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +/** + * org.springframework.web.client.RestTemplate的增强拦截器
+ * + * @author yuzl 俞真龙 + * @since 2022-10-27 + */ +public class RestTemplateInterceptor extends AbstractInterceptor { + private static final int CALLBACK_ARG_LENGTH = 3; + + private static final int CALLBACK_ARG_POSITION = 2; + + @Override + public ExecuteContext before(ExecuteContext context) { + Object[] arguments = context.getArguments(); + + if (arguments != null && arguments.length > CALLBACK_ARG_LENGTH) { + Object argument = arguments[CALLBACK_ARG_POSITION]; + if (argument instanceof RequestCallbackWrapper) { + RequestCallbackWrapper callback = (RequestCallbackWrapper)argument; + parseTags(callback, arguments[0], arguments[1]); + } + } + return context; + } + + private void parseTags(RequestCallbackWrapper callback, Object url, Object method) { + Map header = callback.getHeader(); + if (StringUtils.isBlank(FlowContextUtils.getTagName())) { + return; + } + String encodeTag = header.get(FlowContextUtils.getTagName()); + if (StringUtils.isBlank(encodeTag)) { + return; + } + Map> tags = FlowContextUtils.decodeTags(encodeTag); + if (!tags.isEmpty()) { + ThreadLocalUtils.setRequestData(getRequestData(tags, url, method)); + } + } + + private RequestData getRequestData(Map> tags, Object url, Object method) { + String path = ""; + if (url instanceof URI) { + path = ((URI) url).getPath(); + } + String httpMethod = ""; + if (method instanceof HttpMethod) { + httpMethod = ((HttpMethod) method).name(); + } + return new RequestData(tags, path, httpMethod); + } + + @Override + public ExecuteContext after(ExecuteContext context) throws Exception { + ThreadLocalUtils.removeRequestData(); + return context; + } + + @Override + public ExecuteContext onThrow(ExecuteContext context) throws Exception { + ThreadLocalUtils.removeRequestData(); + return super.onThrow(context); + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/SpringBootLoadClassInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/SpringBootLoadClassInterceptor.java new file mode 100644 index 0000000000..76946bb0f1 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/interceptor/SpringBootLoadClassInterceptor.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.interceptor; + +import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; +import com.huaweicloud.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import com.huaweicloud.sermant.core.utils.ClassUtils; + +/** + * 启动时加载一些必要的类 + * + * @author yuzl 俞真龙 + * @since 2022-10-26 + */ +public class SpringBootLoadClassInterceptor extends AbstractInterceptor { + private static final String REQUEST_CALLBACK_WRAPPER = + "com.huaweicloud.sermant.router.spring.wrapper.RequestCallbackWrapper"; + + @Override + public ExecuteContext before(ExecuteContext context) { + ClassUtils.defineClass(REQUEST_CALLBACK_WRAPPER, getClass().getClassLoader()); + return context; + } + + @Override + public ExecuteContext after(ExecuteContext context) { + return context; + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/wrapper/RequestCallbackWrapper.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/wrapper/RequestCallbackWrapper.java new file mode 100644 index 0000000000..63c7d5fb30 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/com/huaweicloud/sermant/router/spring/wrapper/RequestCallbackWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.sermant.router.spring.wrapper; + +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.web.client.RequestCallback; + +import java.io.IOException; +import java.util.Map; + +/** + * 对于{@link RequestCallback}的一个包装类,用于传递请求头信息
+ * + * @author yuzl 俞真龙 + * @since 2022-10-27 + */ +public class RequestCallbackWrapper implements RequestCallback { + private final RequestCallback callback; + + private final Map header; + + /** + * 构造函数 + * + * @param callback 真实需要包装的对象 + * @param header 请求头信息 + */ + public RequestCallbackWrapper(RequestCallback callback, Map header) { + this.callback = callback; + this.header = header; + } + + @Override + public void doWithRequest(ClientHttpRequest request) throws IOException { + this.callback.doWithRequest(request); + } + + public Map getHeader() { + return header; + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/resources/META-INF/services/com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer b/sermant-plugins/sermant-router/spring-router-plugin/src/main/resources/META-INF/services/com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer index 413a8fc0c6..84a119f113 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/resources/META-INF/services/com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/resources/META-INF/services/com.huaweicloud.sermant.core.plugin.agent.declarer.PluginDeclarer @@ -13,4 +13,7 @@ com.huaweicloud.sermant.router.spring.declarer.ServiceInstanceListSupplierDeclar com.huaweicloud.sermant.router.spring.declarer.ServiceRegistryDeclarer com.huaweicloud.sermant.router.spring.declarer.OkHttp3ClientDeclarer com.huaweicloud.sermant.router.spring.declarer.OkHttpClientDeclarer -com.huaweicloud.sermant.router.spring.declarer.HttpClient4xDeclarer \ No newline at end of file +com.huaweicloud.sermant.router.spring.declarer.HttpClient4xDeclarer +com.huaweicloud.sermant.router.spring.declarer.HttpUrlConnectionConnectDeclarer +com.huaweicloud.sermant.router.spring.declarer.RestTemplateDeclarer +com.huaweicloud.sermant.router.spring.declarer.SpringBootLoadClassDeclarer \ No newline at end of file diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/test/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptorTest.java b/sermant-plugins/sermant-router/spring-router-plugin/src/test/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptorTest.java index 13212e973a..5fc2692216 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/test/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptorTest.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/test/java/com/huaweicloud/sermant/router/spring/interceptor/FeignClientInterceptorTest.java @@ -17,6 +17,8 @@ package com.huaweicloud.sermant.router.spring.interceptor; import com.huaweicloud.sermant.core.plugin.agent.entity.ExecuteContext; +import com.huaweicloud.sermant.core.plugin.config.PluginConfigManager; +import com.huaweicloud.sermant.router.common.config.RouterConfig; import com.huaweicloud.sermant.router.common.request.RequestData; import com.huaweicloud.sermant.router.common.request.RequestHeader; import com.huaweicloud.sermant.router.common.utils.ThreadLocalUtils; @@ -26,9 +28,13 @@ import feign.Request; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.springframework.http.HttpMethod; import java.nio.charset.StandardCharsets; @@ -49,6 +55,8 @@ public class FeignClientInterceptorTest { private final ExecuteContext context; + private static MockedStatic mockStatic; + public FeignClientInterceptorTest() { interceptor = new FeignClientInterceptor(); Object[] arguments = new Object[1]; @@ -60,6 +68,18 @@ public FeignClientInterceptorTest() { context = ExecuteContext.forMemberMethod(new Object(), null, arguments, null, null); } + @BeforeClass + public static void before() { + mockStatic = Mockito.mockStatic(PluginConfigManager.class); + RouterConfig config = new RouterConfig(); + mockStatic.when(() -> PluginConfigManager.getPluginConfig(Mockito.any())).thenReturn(config); + } + + @AfterClass + public static void after() { + mockStatic.close(); + } + /** * 重置测试数据 */ diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/entity/SimpleRequestRecorder.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/entity/SimpleRequestRecorder.java index 65c66a9578..0453af0acd 100644 --- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/entity/SimpleRequestRecorder.java +++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/entity/SimpleRequestRecorder.java @@ -48,7 +48,7 @@ public SimpleRequestRecorder() { @Override public void beforeRequest() { - if (!discoveryPluginConfig.isEnableRequestCount()) { + if (!isEnable()) { return; } final long allRequest = allRequestCount.incrementAndGet(); @@ -61,18 +61,24 @@ public void beforeRequest() { HttpConstants.currentTime(), allRequest)); } + /** + * 是否开启记录 + * + * @return 是否开启记录 + */ + public boolean isEnable() { + return this.discoveryPluginConfig.isEnableRequestCount(); + } + @Override public void errorRequest(Throwable ex, long consumeTimeMs) { - } @Override public void afterRequest(long consumeTimeMs) { - } @Override public void completeRequest() { - } } diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/utils/RequestInterceptorUtils.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/utils/RequestInterceptorUtils.java index d7afd42c72..0017d671a2 100644 --- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/utils/RequestInterceptorUtils.java +++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/com/huawei/discovery/utils/RequestInterceptorUtils.java @@ -16,7 +16,6 @@ package com.huawei.discovery.utils; -import com.huawei.discovery.entity.Recorder; import com.huawei.discovery.entity.ServiceInstance; import com.huawei.discovery.entity.SimpleRequestRecorder; import com.huawei.discovery.retry.InvokerContext; @@ -51,10 +50,9 @@ public class RequestInterceptorUtils { private static final Logger LOGGER = LoggerFactory.getLogger(); - private static final Recorder RECORDER = new SimpleRequestRecorder(); + private static final SimpleRequestRecorder RECORDER = new SimpleRequestRecorder(); private RequestInterceptorUtils() { - } /** @@ -119,6 +117,9 @@ public static Optional rebuildUrlForHttpConnection(URL originUrl, ServiceIn * @param source 请求原, 例如httpclient/http async client */ public static void printRequestLog(String source, Map hostAndPath) { + if (!RECORDER.isEnable()) { + return; + } String path = String.format(Locale.ENGLISH, "/%s%s", hostAndPath.get(HttpConstants.HTTP_URI_HOST), hostAndPath.get(HttpConstants.HTTP_URI_PATH)); LOGGER.log(Level.FINE, String.format(Locale.ENGLISH, "[%s] request [%s] has been intercepted!", source, path)); @@ -172,10 +173,12 @@ public static Supplier buildFunc(Object target, Method method, Object[] InvokerContext invokerContext) { return () -> { try { - AccessController.doPrivileged((PrivilegedAction) () -> { - method.setAccessible(true); - return method; - }); + if (!method.isAccessible()) { + AccessController.doPrivileged((PrivilegedAction) () -> { + method.setAccessible(true); + return method; + }); + } return method.invoke(target, arguments); } catch (IllegalAccessException e) { LOGGER.log(Level.SEVERE, String.format(Locale.ENGLISH, "Can not invoke method [%s]",