You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2019/04/15 12:28:47 UTC

[incubator-skywalking] branch envoy-access-log updated: Finish proxy mesh metric codes.

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch envoy-access-log
in repository https://gitbox.apache.org/repos/asf/incubator-skywalking.git


The following commit(s) were added to refs/heads/envoy-access-log by this push:
     new 663c27a  Finish proxy mesh metric codes.
663c27a is described below

commit 663c27a64c63a07e784b168c13c24783b44afb60
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Mon Apr 15 20:28:35 2019 +0800

    Finish proxy mesh metric codes.
---
 .../envoy-metrics-receiver-plugin/pom.xml          |   6 +
 .../envoy/AccessLogServiceGRPCHandler.java         |  76 ++++++++-
 .../receiver/envoy/EnvoyMetricReceiverConfig.java  |  31 ++++
 .../envoy/EnvoyMetricReceiverProvider.java         |  10 +-
 .../server/receiver/envoy/als/ALSHTTPAnalysis.java |  42 +++++
 .../envoy/als/K8sALSServiceMeshHTTPAnalysis.java   | 175 +++++++++++++++++++++
 .../oap/server/receiver/envoy/als/Role.java        |  37 +++++
 .../server/receiver/envoy/als/ServiceMetaInfo.java |  50 ++++++
 ...g.oap.server.receiver.envoy.als.ALSHTTPAnalysis |  20 +++
 .../receiver/envoy/als/K8sHTTPAnalysisTest.java    | 105 +++++++++++++
 .../src/test/resources/envoy-ingress.msg           |  89 +++++++++++
 .../src/test/resources/envoy-ingress2sidecar.msg   |  99 ++++++++++++
 .../test/resources/envoy-mesh-server-sidecar.msg   |  96 +++++++++++
 .../src/main/assembly/application.yml              |   2 +
 .../src/main/resources/application.yml             |   2 +
 15 files changed, 835 insertions(+), 5 deletions(-)

diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/pom.xml b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/pom.xml
index aa8d8c6..6137f05 100644
--- a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/pom.xml
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/pom.xml
@@ -39,6 +39,12 @@
             <artifactId>skywalking-mesh-receiver-plugin</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java-util</artifactId>
+            <version>3.5.1</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/AccessLogServiceGRPCHandler.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/AccessLogServiceGRPCHandler.java
index 32de937..a48b9c7 100644
--- a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/AccessLogServiceGRPCHandler.java
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/AccessLogServiceGRPCHandler.java
@@ -20,22 +20,92 @@ package org.apache.skywalking.oap.server.receiver.envoy;
 
 import io.envoyproxy.envoy.service.accesslog.v2.*;
 import io.grpc.stub.StreamObserver;
+import java.util.*;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.source.*;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.receiver.envoy.als.*;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.*;
 import org.slf4j.*;
 
 public class AccessLogServiceGRPCHandler extends AccessLogServiceGrpc.AccessLogServiceImplBase {
     private static final Logger logger = LoggerFactory.getLogger(AccessLogServiceGRPCHandler.class);
+    private final List<ALSHTTPAnalysis> envoyHTTPAnalysisList;
+    private final SourceReceiver sourceReceiver;
+    private final CounterMetric counter;
+    private final HistogramMetric histogram;
+    private final CounterMetric sourceDispatcherCounter;
 
-    public AccessLogServiceGRPCHandler(ModuleManager manager) {
+    public AccessLogServiceGRPCHandler(ModuleManager manager, EnvoyMetricReceiverConfig config) {
+        ServiceLoader<ALSHTTPAnalysis> alshttpAnalyses = ServiceLoader.load(ALSHTTPAnalysis.class);
+        envoyHTTPAnalysisList = new ArrayList<>();
+        for (String httpAnalysisName : config.getAlsHTTPAnalysis()) {
+            for (ALSHTTPAnalysis httpAnalysis : alshttpAnalyses) {
+                if (httpAnalysisName.equals(httpAnalysis.name())) {
+                    httpAnalysis.init(config);
+                    envoyHTTPAnalysisList.add(httpAnalysis);
+                }
+            }
+        }
+
+        logger.debug("envoy HTTP analysis: " + envoyHTTPAnalysisList);
 
+        sourceReceiver = manager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
+
+        MetricCreator metricCreator = manager.find(TelemetryModule.NAME).provider().getService(MetricCreator.class);
+        counter = metricCreator.createCounter("envoy_als_in_count", "The count of envoy ALS metric received",
+            MetricTag.EMPTY_KEY, MetricTag.EMPTY_VALUE);
+        histogram = metricCreator.createHistogramMetric("envoy_als_in_latency", "The process latency of service ALS metric receiver",
+            MetricTag.EMPTY_KEY, MetricTag.EMPTY_VALUE);
+        sourceDispatcherCounter = metricCreator.createCounter("envoy_als_source_dispatch_count", "The count of envoy ALS metric received",
+            MetricTag.EMPTY_KEY, MetricTag.EMPTY_VALUE);
     }
 
     public StreamObserver<StreamAccessLogsMessage> streamAccessLogs(
         StreamObserver<StreamAccessLogsResponse> responseObserver) {
         return new StreamObserver<StreamAccessLogsMessage>() {
+            private volatile boolean isFirst = true;
+            private Role role;
+            private StreamAccessLogsMessage.Identifier identifier;
+
             @Override public void onNext(StreamAccessLogsMessage message) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Received msg {}", message);
+                counter.inc();
+
+                HistogramMetric.Timer timer = histogram.createTimer();
+                try {
+                    if (isFirst) {
+                        identifier = message.getIdentifier();
+                        isFirst = false;
+                        role = Role.NONE;
+                        for (ALSHTTPAnalysis analysis : envoyHTTPAnalysisList) {
+                            role = analysis.identify(identifier, role);
+                        }
+                    }
+
+                    StreamAccessLogsMessage.LogEntriesCase logCase = message.getLogEntriesCase();
+
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Messaged is identified from Envoy[{}], role[{}] in [{}]. Received msg {}",
+                            identifier.getNode().getId(), role, logCase, message);
+                    }
+
+                    switch (logCase) {
+                        case HTTP_LOGS:
+                            StreamAccessLogsMessage.HTTPAccessLogEntries logs = message.getHttpLogs();
+
+                            List<Source> sourceResult = new ArrayList<>();
+                            for (ALSHTTPAnalysis analysis : envoyHTTPAnalysisList) {
+                                logs.getLogEntryList().forEach(log -> {
+                                    sourceResult.addAll(analysis.analysis(identifier, log, role));
+                                });
+                            }
+
+                            sourceDispatcherCounter.inc(sourceResult.size());
+                            sourceResult.forEach(sourceReceiver::receive);
+                    }
+                } finally {
+                    timer.finish();
                 }
             }
 
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java
new file mode 100644
index 0000000..d1ec648
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy;
+
+import java.util.List;
+import lombok.Getter;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+
+/**
+ * @author wusheng
+ */
+@Getter
+public class EnvoyMetricReceiverConfig extends ModuleConfig {
+    private List<String> alsHTTPAnalysis;
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverProvider.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverProvider.java
index fce929e..398eac9 100644
--- a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverProvider.java
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverProvider.java
@@ -27,6 +27,12 @@ import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
  * @author wusheng
  */
 public class EnvoyMetricReceiverProvider extends ModuleProvider {
+    private final EnvoyMetricReceiverConfig config;
+
+    public EnvoyMetricReceiverProvider() {
+        config = new EnvoyMetricReceiverConfig();
+    }
+
     @Override public String name() {
         return "default";
     }
@@ -36,7 +42,7 @@ public class EnvoyMetricReceiverProvider extends ModuleProvider {
     }
 
     @Override public ModuleConfig createConfigBeanIfAbsent() {
-        return null;
+        return config;
     }
 
     @Override public void prepare() throws ServiceNotProvidedException, ModuleStartException {
@@ -46,7 +52,7 @@ public class EnvoyMetricReceiverProvider extends ModuleProvider {
     @Override public void start() throws ServiceNotProvidedException, ModuleStartException {
         GRPCHandlerRegister service = getManager().find(CoreModule.NAME).provider().getService(GRPCHandlerRegister.class);
         service.addHandler(new MetricServiceGRPCHandler(getManager()));
-        service.addHandler(new AccessLogServiceGRPCHandler(getManager()));
+        service.addHandler(new AccessLogServiceGRPCHandler(getManager(), config));
     }
 
     @Override public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ALSHTTPAnalysis.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ALSHTTPAnalysis.java
new file mode 100644
index 0000000..1de742d
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ALSHTTPAnalysis.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy.als;
+
+import io.envoyproxy.envoy.data.accesslog.v2.HTTPAccessLogEntry;
+import io.envoyproxy.envoy.service.accesslog.v2.StreamAccessLogsMessage;
+import java.util.List;
+import org.apache.skywalking.oap.server.core.source.Source;
+import org.apache.skywalking.oap.server.receiver.envoy.EnvoyMetricReceiverConfig;
+
+/**
+ * Analysis source metrics from ALS
+ *
+ * @author wusheng
+ */
+public interface ALSHTTPAnalysis {
+    String name();
+
+    void init(EnvoyMetricReceiverConfig config);
+
+    List<Source> analysis(StreamAccessLogsMessage.Identifier identifier,
+        HTTPAccessLogEntry entry, Role role);
+
+    Role identify(StreamAccessLogsMessage.Identifier alsIdentifier,
+        Role prev);
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sALSServiceMeshHTTPAnalysis.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sALSServiceMeshHTTPAnalysis.java
new file mode 100644
index 0000000..3ff7a35
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sALSServiceMeshHTTPAnalysis.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy.als;
+
+import com.google.protobuf.*;
+import io.envoyproxy.envoy.api.v2.core.*;
+import io.envoyproxy.envoy.data.accesslog.v2.*;
+import io.envoyproxy.envoy.service.accesslog.v2.StreamAccessLogsMessage;
+import java.time.Instant;
+import java.util.*;
+import org.apache.skywalking.aop.server.receiver.mesh.TelemetryDataDispatcher;
+import org.apache.skywalking.apm.network.common.DetectPoint;
+import org.apache.skywalking.apm.network.servicemesh.*;
+import org.apache.skywalking.oap.server.core.source.Source;
+import org.apache.skywalking.oap.server.receiver.envoy.EnvoyMetricReceiverConfig;
+import org.slf4j.*;
+
+/**
+ * Analysis log based on
+ *
+ * @author wusheng
+ */
+public class K8sALSServiceMeshHTTPAnalysis implements ALSHTTPAnalysis {
+    private static final Logger logger = LoggerFactory.getLogger(K8sALSServiceMeshHTTPAnalysis.class);
+
+    @Override public String name() {
+        return "k8s-mesh";
+    }
+
+    @Override public void init(EnvoyMetricReceiverConfig config) {
+        //TODO: Start k8s metadata query timer.
+    }
+
+    @Override public List<Source> analysis(StreamAccessLogsMessage.Identifier identifier,
+        HTTPAccessLogEntry entry, Role role) {
+        switch (role) {
+            case PROXY:
+                analysisProxy(identifier, entry);
+            case SIDECAR:
+
+        }
+
+        return Collections.emptyList();
+    }
+
+    
+
+    protected void analysisProxy(StreamAccessLogsMessage.Identifier identifier,
+        HTTPAccessLogEntry entry) {
+        AccessLogCommon properties = entry.getCommonProperties();
+        if (properties != null) {
+            Address downstreamLocalAddress = properties.getDownstreamLocalAddress();
+            Address downstreamRemoteAddress = properties.getDownstreamRemoteAddress();
+            Address upstreamRemoteAddress = properties.getUpstreamRemoteAddress();
+            if (downstreamLocalAddress != null && downstreamRemoteAddress != null && upstreamRemoteAddress != null) {
+                SocketAddress downstreamRemoteAddressSocketAddress = downstreamRemoteAddress.getSocketAddress();
+                ServiceMetaInfo outside = find(downstreamRemoteAddressSocketAddress.getAddress(), downstreamRemoteAddressSocketAddress.getPortValue());
+
+                SocketAddress downstreamLocalAddressSocketAddress = downstreamLocalAddress.getSocketAddress();
+                ServiceMetaInfo ingress = find(downstreamLocalAddressSocketAddress.getAddress(), downstreamLocalAddressSocketAddress.getPortValue());
+
+                long startTime = formatAsLong(properties.getStartTime());
+                long duration = formatAsLong(properties.getTimeToLastDownstreamTxByte());
+
+                HTTPRequestProperties request = entry.getRequest();
+                String endpoint = "/";
+                Protocol protocol = Protocol.HTTP;
+                if (request != null) {
+                    endpoint = request.getPath();
+                    String schema = request.getScheme();
+                    if (schema.equals("http") || schema.equals("https")) {
+                        protocol = Protocol.HTTP;
+                    } else {
+                        protocol = Protocol.gRPC;
+                    }
+                }
+                HTTPResponseProperties response = entry.getResponse();
+                int responseCode = 200;
+                if (response != null) {
+                    responseCode = response.getResponseCode().getValue();
+                }
+                boolean status = responseCode >= 200 && responseCode < 400;
+
+                ServiceMeshMetric metric = ServiceMeshMetric.newBuilder().setStartTime(startTime)
+                    .setEndTime(startTime + duration)
+                    .setSourceServiceName(outside.getServiceName())
+                    .setSourceServiceInstance(outside.getServiceInstanceName())
+                    .setDestServiceName(ingress.getServiceName())
+                    .setDestServiceInstance(ingress.getServiceInstanceName())
+                    .setEndpoint(endpoint).setLatency((int)duration)
+                    .setResponseCode(Math.toIntExact(responseCode))
+                    .setStatus(status).setProtocol(protocol)
+                    .setDetectPoint(DetectPoint.server)
+                    .build();
+
+                logger.debug("Transformed ingress inbound mesh metric {}", metric);
+                forward(metric);
+
+                SocketAddress upstreamRemoteAddressSocketAddress = upstreamRemoteAddress.getSocketAddress();
+                ServiceMetaInfo targetService = find(upstreamRemoteAddressSocketAddress.getAddress(), upstreamRemoteAddressSocketAddress.getPortValue());
+
+                long outboundStartTime = startTime + formatAsLong(properties.getTimeToFirstUpstreamTxByte());
+                long outboundEndTime = startTime + formatAsLong(properties.getTimeToLastUpstreamRxByte());
+
+                ServiceMeshMetric outboundMetric = ServiceMeshMetric.newBuilder().setStartTime(outboundStartTime)
+                    .setEndTime(outboundEndTime)
+                    .setSourceServiceName(ingress.getServiceName())
+                    .setSourceServiceInstance(ingress.getServiceInstanceName())
+                    .setDestServiceName(targetService.getServiceName())
+                    .setDestServiceInstance(targetService.getServiceInstanceName())
+                    .setEndpoint(endpoint).setLatency((int)(outboundEndTime - outboundStartTime))
+                    .setResponseCode(Math.toIntExact(responseCode))
+                    .setStatus(status).setProtocol(protocol)
+                    .setDetectPoint(DetectPoint.client)
+                    .build();
+
+                logger.debug("Transformed ingress outbound mesh metric {}", outboundMetric);
+                forward(outboundMetric);
+            }
+        }
+    }
+
+    @Override public Role identify(StreamAccessLogsMessage.Identifier alsIdentifier,
+        Role prev) {
+        if (alsIdentifier != null) {
+            Node node = alsIdentifier.getNode();
+            if (node != null) {
+                String id = node.getId();
+                if (id.startsWith("router~")) {
+                    return Role.PROXY;
+                } else if (id.startsWith("sidecar~")) {
+                    return Role.SIDECAR;
+                }
+            }
+        }
+
+        return prev;
+    }
+
+    protected ServiceMetaInfo find(String ip, int port) {
+        //TODO: go through API server to get target service info
+        // If can't get service or service instance name, set `UNKNOWN` string.
+        // Service instance name is pod name
+        // Service name should be deployment name
+        throw new UnsupportedOperationException("TODO");
+    }
+
+    protected void forward(ServiceMeshMetric metric) {
+        TelemetryDataDispatcher.preProcess(metric);
+    }
+
+    private long formatAsLong(Timestamp timestamp) {
+        return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()).toEpochMilli();
+    }
+
+    private long formatAsLong(Duration duration) {
+        return Instant.ofEpochSecond(duration.getSeconds(), duration.getNanos()).toEpochMilli();
+    }
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/Role.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/Role.java
new file mode 100644
index 0000000..8842c39
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/Role.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy.als;
+
+/**
+ * The role of envoy in this RPC.
+ */
+public enum Role {
+    /**
+     * Can't identify
+     */
+    NONE,
+    /**
+     * Proxy, such as Ingress, or not mesh
+     */
+    PROXY,
+    /**
+     * Sidecar in mesh
+     */
+    SIDECAR
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ServiceMetaInfo.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ServiceMetaInfo.java
new file mode 100644
index 0000000..9294a75
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/ServiceMetaInfo.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy.als;
+
+import java.util.List;
+import lombok.*;
+
+/**
+ * @author wusheng
+ */
+@Getter
+@Setter
+public class ServiceMetaInfo {
+    private String serviceName;
+    private String serviceInstanceName;
+    private List<KeyValue> tags;
+
+    public ServiceMetaInfo() {
+    }
+
+    public ServiceMetaInfo(String serviceName, String serviceInstanceName) {
+        this.serviceName = serviceName;
+        this.serviceInstanceName = serviceInstanceName;
+    }
+
+    @Setter
+    @Getter
+    public static class KeyValue {
+        private String key;
+        private String value;
+    }
+
+    public static final ServiceMetaInfo UNKNOWN = new ServiceMetaInfo("UNKNOWN", "UNKNOWN");
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.receiver.envoy.als.ALSHTTPAnalysis b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.receiver.envoy.als.ALSHTTPAnalysis
new file mode 100644
index 0000000..c744de5
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.receiver.envoy.als.ALSHTTPAnalysis
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+#
+
+
+org.apache.skywalking.oap.server.receiver.envoy.als.K8sALSServiceMeshHTTPAnalysis
\ No newline at end of file
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sHTTPAnalysisTest.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sHTTPAnalysisTest.java
new file mode 100644
index 0000000..ff6e016
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/envoy/als/K8sHTTPAnalysisTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.envoy.als;
+
+import com.google.protobuf.util.JsonFormat;
+import io.envoyproxy.envoy.service.accesslog.v2.StreamAccessLogsMessage;
+import java.io.*;
+import java.util.*;
+import org.apache.skywalking.apm.network.servicemesh.ServiceMeshMetric;
+import org.apache.skywalking.oap.server.receiver.envoy.MetricServiceGRPCHandlerTestMain;
+import org.junit.*;
+
+public class K8sHTTPAnalysisTest {
+    @Test
+    public void testIngressRoleIdentify() throws IOException {
+        MockK8sAnalysis analysis = new MockK8sAnalysis();
+        try (InputStreamReader isr = new InputStreamReader(getResourceAsStream("envoy-ingress.msg"))) {
+            StreamAccessLogsMessage.Builder requestBuilder = StreamAccessLogsMessage.newBuilder();
+            JsonFormat.parser().merge(isr, requestBuilder);
+            Role identify = analysis.identify(requestBuilder.getIdentifier(), Role.NONE);
+
+            Assert.assertEquals(Role.PROXY, identify);
+        }
+    }
+
+    @Test
+    public void testSidecarRoleIdentify() throws IOException {
+        MockK8sAnalysis analysis = new MockK8sAnalysis();
+        try (InputStreamReader isr = new InputStreamReader(getResourceAsStream("envoy-mesh-server-sidecar.msg"))) {
+            StreamAccessLogsMessage.Builder requestBuilder = StreamAccessLogsMessage.newBuilder();
+            JsonFormat.parser().merge(isr, requestBuilder);
+            Role identify = analysis.identify(requestBuilder.getIdentifier(), Role.NONE);
+
+            Assert.assertEquals(Role.SIDECAR, identify);
+        }
+    }
+
+    @Test
+    public void testIngressMetric() throws IOException {
+        MockK8sAnalysis analysis = new MockK8sAnalysis();
+        try (InputStreamReader isr = new InputStreamReader(getResourceAsStream("envoy-ingress.msg"))) {
+            StreamAccessLogsMessage.Builder requestBuilder = StreamAccessLogsMessage.newBuilder();
+            JsonFormat.parser().merge(isr, requestBuilder);
+
+            analysis.analysis(requestBuilder.getIdentifier(), requestBuilder.getHttpLogs().getLogEntry(0), Role.PROXY);
+
+            Assert.assertEquals(2, analysis.metrics.size());
+
+            ServiceMeshMetric incoming = analysis.metrics.get(0);
+            Assert.assertEquals("UNKNOWN", incoming.getSourceServiceName());
+            Assert.assertEquals("ingress", incoming.getDestServiceName());
+
+            ServiceMeshMetric outgoing = analysis.metrics.get(1);
+            Assert.assertEquals("ingress", outgoing.getSourceServiceName());
+            Assert.assertEquals("productpage", outgoing.getDestServiceName());
+        }
+    }
+
+    public static class MockK8sAnalysis extends K8sALSServiceMeshHTTPAnalysis {
+        private List<ServiceMeshMetric> metrics = new ArrayList<>();
+
+        @Override
+        protected void forward(ServiceMeshMetric metric) {
+            metrics.add(metric);
+        }
+
+        @Override
+        protected ServiceMetaInfo find(String ip, int port) {
+            switch (ip) {
+                case "10.44.2.56":
+                    return new ServiceMetaInfo("ingress", "ingress-Inst");
+                case "10.44.2.54":
+                    return new ServiceMetaInfo("productpage", "productpage-Inst");
+                case "10.44.6.66":
+                    return new ServiceMetaInfo("detail", "detail-Inst");
+            }
+            return ServiceMetaInfo.UNKNOWN;
+        }
+    }
+
+    private static InputStream getResourceAsStream(final String resource) {
+        final InputStream in = getContextClassLoader().getResourceAsStream(resource);
+        return in == null ? MetricServiceGRPCHandlerTestMain.class.getResourceAsStream(resource) : in;
+    }
+
+    private static ClassLoader getContextClassLoader() {
+        return Thread.currentThread().getContextClassLoader();
+    }
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress.msg b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress.msg
new file mode 100644
index 0000000..0f4917e
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress.msg
@@ -0,0 +1,89 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+
+{
+    "identifier": {
+        "node": {
+            "id": "router~10.44.2.56~istio-ingressgateway-699c7dc774-hjxq5.istio-system~istio-system.svc.cluster.local",
+            "cluster": "istio-ingressgateway",
+            "metadata": {
+                "CONFIG_NAMESPACE": "istio-system",
+                "ISTIO_META_INSTANCE_IPS": "10.44.2.56,10.44.2.56,fe80::9ca5:e5ff:fede:6414",
+                "ISTIO_PROXY_SHA": "istio-proxy:55c80965eab994e6bfa2227e3942fa89928d0d70",
+                "ISTIO_PROXY_VERSION": "1.1.0",
+                "ISTIO_VERSION": "1.0-dev",
+                "POD_NAME": "istio-ingressgateway-699c7dc774-hjxq5",
+                "ROUTER_MODE": "sni-dnat",
+                "istio": "sidecar"
+            },
+            "locality": { },
+            "buildVersion": "55c80965eab994e6bfa2227e3942fa89928d0d70/1.10.0-dev/Clean/RELEASE/BoringSSL"
+        },
+        "logName": "als"
+    },
+    "httpLogs": {
+        "logEntry": [
+            {
+                "commonProperties": {
+                    "downstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "10.138.0.14",
+                            "portValue": 51489
+                        }
+                    },
+                    "downstreamLocalAddress": {
+                        "socketAddress": {
+                            "address": "10.44.2.56",
+                            "portValue": 80
+                        }
+                    },
+                    "startTime": "2019-04-13T03:59:53.687224601Z",
+                    "timeToLastRxByte": "0.000031206s",
+                    "timeToFirstUpstreamTxByte": "0.000869250s",
+                    "timeToLastUpstreamTxByte": "0.000881276s",
+                    "timeToFirstUpstreamRxByte": "1.010010710s",
+                    "timeToLastUpstreamRxByte": "1.010423815s",
+                    "timeToFirstDownstreamTxByte": "1.010053396s",
+                    "timeToLastDownstreamTxByte": "1.010432910s",
+                    "upstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "10.44.2.54",
+                            "portValue": 9080
+                        }
+                    },
+                    "upstreamCluster": "outbound|9080||productpage.default.svc.cluster.local"
+                },
+                "protocolVersion": "HTTP11",
+                "request": {
+                    "requestMethod": "GET",
+                    "scheme": "http",
+                    "authority": "35.227.162.132",
+                    "path": "/productpage",
+                    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15",
+                    "forwardedFor": "10.138.0.14",
+                    "requestId": "0ac1feff-84ae-4d3a-8b15-890da2b194c5",
+                    "requestHeadersBytes": "1038"
+                },
+                "response": {
+                    "responseCode": 200,
+                    "responseHeadersBytes": "147",
+                    "responseBodyBytes": "4415"
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress2sidecar.msg b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress2sidecar.msg
new file mode 100644
index 0000000..b641b14
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-ingress2sidecar.msg
@@ -0,0 +1,99 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+{
+    // Mock identifier
+    "identifier": {
+            "node": {
+                "id": "sidecar~10.44.2.54~product-v1-d66dcfdc5-kh6v7.default~default.svc.cluster.local",
+                "cluster": "product.default",
+                "metadata": {
+                    "CONFIG_NAMESPACE": "default",
+                    "INTERCEPTION_MODE": "REDIRECT",
+                    "ISTIO_META_INSTANCE_IPS": "10.44.2.54,10.44.2.54,fe80::d8e8:b6ff:fed6:f857",
+                    "ISTIO_PROXY_SHA": "istio-proxy:55c80965eab994e6bfa2227e3942fa89928d0d70",
+                    "ISTIO_PROXY_VERSION": "1.1.0",
+                    "ISTIO_VERSION": "1.0-dev",
+                    "POD_NAME": "product-v1-d66dcfdc5-kh6v7",
+                    "app": "product",
+                    "istio": "sidecar",
+                    "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container istio-proxy; cpu request for container reviews",
+                    "pod-template-hash": "822879871",
+                    "version": "v1"
+                },
+                "locality": { },
+                "buildVersion": "55c80965eab994e6bfa2227e3942fa89928d0d70/1.10.0-dev/Clean/RELEASE/BoringSSL"
+            },
+            "logName": "als"
+        },
+    // Real log sample
+    "httpLogs": {
+        "logEntry": [
+            {
+                "commonProperties": {
+                    "downstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "10.138.0.14",
+                            "portValue": 0
+                        }
+                    },
+                    "downstreamLocalAddress": {
+                        "socketAddress": {
+                            "address": "10.44.2.54",
+                            "portValue": 9080
+                        }
+                    },
+                    "startTime": "2019-04-13T03:59:53.688609181Z",
+                    "timeToLastRxByte": "0.000081758s",
+                    "timeToFirstUpstreamTxByte": "0.000789220s",
+                    "timeToLastUpstreamTxByte": "0.000808326s",
+                    "timeToFirstUpstreamRxByte": "1.008120501s",
+                    "timeToLastUpstreamRxByte": "1.008369826s",
+                    "timeToFirstDownstreamTxByte": "1.008242458s",
+                    "timeToLastDownstreamTxByte": "1.008378251s",
+                    "upstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "127.0.0.1",
+                            "portValue": 9080
+                        }
+                    },
+                    "upstreamCluster": "inbound|9080|http|productpage.default.svc.cluster.local",
+                    "metadata": {
+                        "filterMetadata": {
+                            "istio_authn": { }
+                        }
+                    }
+                },
+                "protocolVersion": "HTTP11",
+                "request": {
+                    "requestMethod": "GET",
+                    "scheme": "http",
+                    "authority": "35.227.162.132",
+                    "path": "/productpage",
+                    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15",
+                    "forwardedFor": "10.138.0.14",
+                    "requestId": "0ac1feff-84ae-4d3a-8b15-890da2b194c5",
+                    "requestHeadersBytes": "579"
+                },
+                "response": {
+                    "responseCode": 200,
+                    "responseHeadersBytes": "147",
+                    "responseBodyBytes": "4415"
+                }
+            }
+        ]
+    }
+}
diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-mesh-server-sidecar.msg b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-mesh-server-sidecar.msg
new file mode 100644
index 0000000..d02ce2b
--- /dev/null
+++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/test/resources/envoy-mesh-server-sidecar.msg
@@ -0,0 +1,96 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+{
+    "identifier": {
+        "node": {
+            "id": "sidecar~10.44.2.55~reviews-v1-d66dcfdc5-kh6v7.default~default.svc.cluster.local",
+            "cluster": "reviews.default",
+            "metadata": {
+                "CONFIG_NAMESPACE": "default",
+                "INTERCEPTION_MODE": "REDIRECT",
+                "ISTIO_META_INSTANCE_IPS": "10.44.2.55,10.44.2.55,fe80::d8e8:b6ff:fed6:f857",
+                "ISTIO_PROXY_SHA": "istio-proxy:55c80965eab994e6bfa2227e3942fa89928d0d70",
+                "ISTIO_PROXY_VERSION": "1.1.0",
+                "ISTIO_VERSION": "1.0-dev",
+                "POD_NAME": "reviews-v1-d66dcfdc5-kh6v7",
+                "app": "reviews",
+                "istio": "sidecar",
+                "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container istio-proxy; cpu request for container reviews",
+                "pod-template-hash": "822879871",
+                "version": "v1"
+            },
+            "locality": { },
+            "buildVersion": "55c80965eab994e6bfa2227e3942fa89928d0d70/1.10.0-dev/Clean/RELEASE/BoringSSL"
+        },
+        "logName": "als"
+    },
+    "httpLogs": {
+        "logEntry": [
+            {
+                "commonProperties": {
+                    "downstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "10.44.2.54",
+                            "portValue": 58356
+                        }
+                    },
+                    "downstreamLocalAddress": {
+                        "socketAddress": {
+                            "address": "10.44.2.55",
+                            "portValue": 9080
+                        }
+                    },
+                    "startTime": "2019-04-13T03:59:53.712690678Z",
+                    "timeToLastRxByte": "0.000127695s",
+                    "timeToFirstUpstreamTxByte": "0.000841545s",
+                    "timeToLastUpstreamTxByte": "0.000854020s",
+                    "timeToFirstUpstreamRxByte": "0.977617052s",
+                    "timeToLastUpstreamRxByte": "0.977797037s",
+                    "timeToFirstDownstreamTxByte": "0.977764621s",
+                    "timeToLastDownstreamTxByte": "0.977811534s",
+                    "upstreamRemoteAddress": {
+                        "socketAddress": {
+                            "address": "127.0.0.1",
+                            "portValue": 9080
+                        }
+                    },
+                    "upstreamCluster": "inbound|9080|http|reviews.default.svc.cluster.local",
+                    "metadata": {
+                        "filterMetadata": {
+                            "istio_authn": { }
+                        }
+                    }
+                },
+                "protocolVersion": "HTTP11",
+                "request": {
+                    "requestMethod": "GET",
+                    "scheme": "http",
+                    "authority": "reviews:9080",
+                    "path": "/reviews/0",
+                    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15",
+                    "requestId": "0ac1feff-84ae-4d3a-8b15-890da2b194c5",
+                    "requestHeadersBytes": "423"
+                },
+                "response": {
+                    "responseCode": 200,
+                    "responseHeadersBytes": "181",
+                    "responseBodyBytes": "295"
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/oap-server/server-starter/src/main/assembly/application.yml b/oap-server/server-starter/src/main/assembly/application.yml
index 66ac949..a298eb0 100644
--- a/oap-server/server-starter/src/main/assembly/application.yml
+++ b/oap-server/server-starter/src/main/assembly/application.yml
@@ -98,6 +98,8 @@ istio-telemetry:
   default:
 envoy-metric:
   default:
+    alsHTTPAnalysis:
+      - k8s-mesh
 #receiver_zipkin:
 #  default:
 #    host: ${SW_RECEIVER_ZIPKIN_HOST:0.0.0.0}
diff --git a/oap-server/server-starter/src/main/resources/application.yml b/oap-server/server-starter/src/main/resources/application.yml
index c1c14bd..25b12f5 100644
--- a/oap-server/server-starter/src/main/resources/application.yml
+++ b/oap-server/server-starter/src/main/resources/application.yml
@@ -98,6 +98,8 @@ istio-telemetry:
   default:
 envoy-metric:
   default:
+    alsHTTPAnalysis:
+      - k8s-mesh
 #receiver_zipkin:
 #  default:
 #    host: ${SW_RECEIVER_ZIPKIN_HOST:0.0.0.0}