Skip to content

Commit

Permalink
refactor : http to grpc
Browse files Browse the repository at this point in the history
- 기존 HTTP 로 받던 Trace 및 Metric 데이터를 GRPC로 리팩토링
  • Loading branch information
JiwonKKang committed Oct 28, 2024
1 parent 5b1e7cd commit 5d92e8b
Show file tree
Hide file tree
Showing 19 changed files with 375 additions and 15 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ build/
!**/src/main/**/build/
!**/src/test/**/build/

/kafka/volumes
/kafka/.env
infra/kafka/volumes
infra/kafka/.env
### STS ###
.apt_generated
.classpath
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
start:
docker compose -f kafka/common.yml -f kafka/zookeeper.yml -f kafka/kafka_cluster.yml up -d
docker compose -f es/docker-compose.yml up -d
docker compose -f infra/kafka/common.yml -f infra/kafka/zookeeper.yml -f infra/kafka/kafka_cluster.yml up -d
docker compose -f infra/es/docker-compose.yml up -d

stop:
docker compose -f kafka/common.yml -f kafka/zookeeper.yml -f kafka/kafka_cluster.yml down
docker compose -f es/docker-compose.yml down
docker compose -f infra/kafka/common.yml -f infra/kafka/zookeeper.yml -f infra/kafka/kafka_cluster.yml down
docker compose -f infra/es/docker-compose.yml down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
44 changes: 44 additions & 0 deletions infra/nginx/conf.d/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
listen 80;
http2 on;
server_name tracedin.p-e.kr;
server_tokens off;

location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
return 301 https://$server_name$request_uri;
}
}

server {
listen 443 ssl;
http2 on;
server_name tracedin.p-e.kr;
server_tokens off;

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;

ssl_certificate /etc/letsencrypt/live/tracedin.p-e.kr/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/tracedin.p-e.kr/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

location / {
proxy_pass http://tracedin-green;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header Connection '';
proxy_http_version 1.1;
}

location /SpanGrpcAppender/AppendSpans {
grpc_pass grpc://grpcserver;
}

location /ServiceMetricsGrpcAppender/AppendServiceMetrics {
grpc_pass grpc://grpcserver;
}
}
9 changes: 9 additions & 0 deletions infra/nginx/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
nginx:
image: nginx:latest
container_name: nginx
ports:
- "80:80"
volumes:
- ./conf.d:/etc/nginx/conf.d # Nginx 설정 파일
- ./nginx/log:/var/log/nginx # 로그 파일
50 changes: 49 additions & 1 deletion tracedin-application/app-api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
buildscript {
ext {
protobufVersion = '3.21.5'
protobufPluginVersion = '0.9.4'
grpcVersion = '1.65.1'
}
}


plugins {
// Protobuf 플러그인을 적용하여 .proto 파일을 컴파일할 수 있다. 여기서 버전은 ext에 정의된 protobufPluginVersion을 사용한다.
id 'com.google.protobuf' version "${protobufPluginVersion}"
}

dependencies {

implementation project(':tracedin-domain')
Expand All @@ -22,4 +36,38 @@ dependencies {

//web
implementation 'org.springframework.boot:spring-boot-starter-web'
}

/// grpc 서버, 클라이언트 설정
implementation 'net.devh:grpc-spring-boot-starter:3.1.0.RELEASE'
// Spring Boot와 gRPC의 통합을 간편하게 도와주는 스타터
implementation "io.grpc:grpc-netty-shaded:${grpcVersion}"
// Netty Shaded 사용(gRPC 서버와 클라이언트의 Netty 전송 계층을 제공)
implementation "io.grpc:grpc-protobuf:${grpcVersion}" // Protobuf 메시지와 gRPC의 통합을 지원
implementation "io.grpc:grpc-stub:${grpcVersion}" // gRPC 클라이언트 스텁을 생성
compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
// 이걸 추가해야 gRPC 컴파일시 javax 어노테이션 오류가 발생하지 않는다.
}

protobuf {
// Protobuf 컴파일러를 지정하여 .proto 파일을 컴파일합니다.
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
// 생성된 파일을 정리합니다.
clean {
delete generatedFilesBaseDir
}
// gRPC 플러그인을 설정하여 Protobuf 파일로부터 gRPC 관련 코드를 생성합니다.
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
// 모든 프로토콜 버퍼 작업에 대해 gRPC 플러그인을 적용합니다.
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.univ.tracedin.api.metric.grpc;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import com.univ.tracedin.api.metric.grpc.ServiceMetricsGrpcAppenderGrpc.ServiceMetricsGrpcAppenderImplBase;
import com.univ.tracedin.api.metric.grpc.ServiceMetricsProto.AppendServiceMetricsRequest;
import com.univ.tracedin.api.metric.grpc.ServiceMetricsProto.AppendServiceMetricsResponse;
import com.univ.tracedin.api.metric.grpc.ServiceMetricsProto.MetricRequest;
import com.univ.tracedin.domain.metric.Metric;
import com.univ.tracedin.domain.metric.MetricType;
import com.univ.tracedin.domain.metric.ServiceMetrics;
import com.univ.tracedin.domain.metric.ServiceMetricsService;
import com.univ.tracedin.domain.project.ProjectKey;

import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@Slf4j
@RequiredArgsConstructor
@GrpcService
public class ServiceMetricsGrpc extends ServiceMetricsGrpcAppenderImplBase {

private final ServiceMetricsService serviceMetricsService;

@Override
public void appendServiceMetrics(
AppendServiceMetricsRequest request,
StreamObserver<AppendServiceMetricsResponse> responseObserver) {
try {
log.info("appendServiceMetrics request: {}", request.toString());
serviceMetricsService.appendMetrics(toServiceMetrics(request));
responseObserver.onNext(
AppendServiceMetricsResponse.newBuilder().setStatusCode(200).build());
} catch (Exception e) {
log.error("Failed to append service metrics", e);
responseObserver.onNext(
AppendServiceMetricsResponse.newBuilder().setStatusCode(500).build());
} finally {
responseObserver.onCompleted();
}
}

private ServiceMetrics toServiceMetrics(AppendServiceMetricsRequest request) {
return ServiceMetrics.builder()
.projectKey(ProjectKey.from(request.getProjectKey()))
.serviceName(request.getServiceName())
.metrics(request.getMetricsList().stream().map(this::toMetric).toList())
.build();
}

private Metric toMetric(MetricRequest metricRequest) {
return Metric.builder()
.name(metricRequest.getName())
.description(metricRequest.getDescription())
.unit(metricRequest.getUnit())
.type(MetricType.fromValue(metricRequest.getType()))
.value(metricRequest.getValue())
.count(metricRequest.getCount())
.sum(metricRequest.getSum())
.min(metricRequest.getMin())
.max(metricRequest.getMax())
.attributes(
metricRequest.getAttributesMap().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
.timestamp(LocalDateTime.now())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import com.univ.tracedin.domain.span.SpanType;
import com.univ.tracedin.domain.span.TraceId;

import io.micrometer.common.util.StringUtils;

public record AppendSpanRequest(
String serviceName,
String projectKey,
Expand Down Expand Up @@ -68,7 +70,8 @@ public Span toSpan() {
}

private SpanType getSpanType() {
SpanType type = spanType == null ? SpanType.UNKNOWN : SpanType.fromValue(spanType);
SpanType type =
StringUtils.isBlank(spanType) ? SpanType.UNKNOWN : SpanType.fromValue(spanType);
if (attributes.data().containsKey("db.operation")) {
type = SpanType.QUERY;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.univ.tracedin.api.span.grpc;

import java.util.Map;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import com.univ.tracedin.api.span.grpc.SpanGrpcAppenderGrpc.SpanGrpcAppenderImplBase;
import com.univ.tracedin.api.span.grpc.SpanProto.AppendSpanResponse;
import com.univ.tracedin.api.span.grpc.SpanProto.AppendSpansRequest;
import com.univ.tracedin.domain.span.Span;
import com.univ.tracedin.domain.span.SpanAttributes;
import com.univ.tracedin.domain.span.SpanEvent;
import com.univ.tracedin.domain.span.SpanId;
import com.univ.tracedin.domain.span.SpanKind;
import com.univ.tracedin.domain.span.SpanService;
import com.univ.tracedin.domain.span.SpanStatus;
import com.univ.tracedin.domain.span.SpanTiming;
import com.univ.tracedin.domain.span.SpanType;
import com.univ.tracedin.domain.span.TraceId;

import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@Slf4j
@RequiredArgsConstructor
@GrpcService
public class SpanGrpc extends SpanGrpcAppenderImplBase {

private final SpanService spanService;

@Override
public void appendSpans(
AppendSpansRequest request, StreamObserver<AppendSpanResponse> responseObserver) {
try {
log.info("appendSpans request: {}", request.toString());
spanService.publishSpans(request.getSpansList().stream().map(this::toSpan).toList());
responseObserver.onNext(AppendSpanResponse.newBuilder().setStatusCode(200).build());
} catch (Exception e) {
log.error("Failed to append spans", e);
responseObserver.onNext(AppendSpanResponse.newBuilder().setStatusCode(500).build());
} finally {
responseObserver.onCompleted();
}
}

private Span toSpan(SpanProto.Span span) {
return Span.builder()
.id(SpanId.from(span.getSpanId()))
.traceId(TraceId.from(span.getTraceId()))
.parentId(SpanId.from(span.getParentSpanId()))
.name(span.getName())
.serviceName(span.getServiceName())
.projectKey(span.getProjectKey())
.spanType(SpanType.fromValue(span.getSpanType()))
.kind(SpanKind.fromValue(span.getKind()))
.timing(
SpanTiming.builder()
.startEpochMillis(nanosToMillis(span.getStartEpochNanos()))
.endEpochMillis(nanosToMillis(span.getEndEpochNanos()))
.build())
.status(SpanStatus.fromValue(span.getSpanStatus()))
.attributes(
SpanAttributes.builder()
.data(
span.getAttributes().getDataMap().entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue)))
.capacity(span.getAttributes().getCapacity())
.totalAddedValues(span.getAttributes().getTotalAddedValues())
.build())
.events(
span.getEventsList().stream()
.map(
event ->
new SpanEvent(
event.getName(),
event.getAttributesMap().entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry
::getValue)),
event.getEpochNanos()))
.toList())
.build();
}

private long nanosToMillis(long nanos) {
return nanos / 1_000_000;
}
}
31 changes: 31 additions & 0 deletions tracedin-application/app-api/src/main/proto/service-metrics.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

option java_package = "com.univ.tracedin.api.metric.grpc";
option java_outer_classname = "ServiceMetricsProto";

message AppendServiceMetricsRequest {
string project_key = 1;
string service_name = 2;
repeated MetricRequest metrics = 3;
}

message MetricRequest {
string name = 1;
string description = 2;
string unit = 3;
string type = 4;
double value = 5; // GAUGE 및 SUM 타입의 단일 값
int64 count = 6; // HISTOGRAM 타입의 카운트
double sum = 7; // HISTOGRAM 타입의 합계
double min = 8; // HISTOGRAM 타입의 최소값
double max = 9; // HISTOGRAM 타입의 최대값
map<string, string> attributes = 10; // 속성 정보
}
message AppendServiceMetricsResponse {
int64 status_code = 1;
}


service ServiceMetricsGrpcAppender {
rpc AppendServiceMetrics (AppendServiceMetricsRequest) returns (AppendServiceMetricsResponse);
}
Loading

0 comments on commit 5d92e8b

Please sign in to comment.