Apollo Studio와 GraphQL을 통합합니다.네트워크 - 섹션 1
질문
부가 층을 추가하면 일련의 새로운 문제를 해결해야 한다. 가장 흔히 볼 수 있는 문제는 다음과 같다.
입출력 작업 수
하나의 도형 조회는 간단해 보이지만 여러 API, 읽기/쓰기 데이터 저장소를 호출하는 여러 구역, 심지어 제3자 공급자까지 호출할 수 있다.백엔드가 너무 복잡하거나 백엔드에서 너무 많은 데이터를 추출했기 때문일 수 있다.
총 지속 시간(성과)
일반적으로 이것은 상술한 작업의 부작용일 수 있지만, 입출력 작업은 조회를 아래로 드래그할 수 있다.조건이나 데이터 세트가 올바르면 API 호출이 실행되는 SQL 쿼리의 최적화 수준이 낮아 30초 이상 걸릴 수 있습니다.
잘못
로그 기록은 추적 오류에 매우 유용하지만, 오류가 발생한 조회와 단일 추적의 통계 정보를 보는 데는 더욱 복잡한 작업일 수 있으며, 로그에서 생성해야 한다.
설정 모니터링
Apollo GraphQL은 매우 흔히 볼 수 있는 커뮤니티가 구동하는 GraphQL 구현으로 주로 NodeJ에 사용된다.핵심 라이브러리를 제외하고 그들은 무료(비용 지불 부가 기능 제공)의saaS 플랫폼을 제공하여GraphQL 실현을 감시하는 데 사용한다:Apollo Studio.이러한 솔루션은 다음과 같은 기능을 제공합니다.
Apollo GraphQL은 매우 흔히 볼 수 있는 커뮤니티가 구동하는 GraphQL 구현으로 주로 NodeJ에 사용된다.핵심 라이브러리를 제외하고 그들은 무료(비용 지불 부가 기능 제공)의saaS 플랫폼을 제공하여GraphQL 실현을 감시하는 데 사용한다:Apollo Studio.이러한 솔루션은 다음과 같은 기능을 제공합니다.
아폴로 스튜디오에 통합
Apollo 서버GraphQL 구현(NodeJS에 사용) 사용자에게 매우 아름답습니다straight-forward.자바와 파이썬의 실현에는 제3자 공급자도 있지만 이것이 지원의 종점이다.위의 링크는 어떻게 사용하는지create a custom integration도 상세하게 소개했는데 이것이 본고의 중점이다.이 과정은 protobuf 모드를 가져오고 성능 통계 데이터를 Apollo 추적 형식으로 변환하며 메시지에 서명하고 마지막으로 백엔드 프로세스로 변환하여 일괄 처리하는 것을 포함한다.
Protobuf를 위한 아폴로 스튜디오 과정 생성
많은 Protobuf의 실현NET 내장 핵, 그러나 나는 protobuf-net를 좋아한다. 왜냐하면 그것은 괜찮고 깨끗하며Apache 2.0의 허가를 받은 실현이기 때문이다.그것 또한 protogen 지원을 받았습니다. 이것은 훌륭한 온라인 생성기입니다. 프로토버프 넷 클래스를 출력해서 사용할 수 있습니다. (CSharp 프로필에 사용됩니다.)링크here에서 최신 모드를 열면 생성기에 붙여넣기만 하면 됩니다.
주의: 본문을 작성할 때 [(js_preEncoded)=true]
생성기 지원을 받지 않으며 프로토 모드에서 삭제할 수 있습니다.
아폴로 스튜디오 형식으로 변환
아폴로에 적합한 형식으로 데이터를 얻기 위해 enable Apollo Tracing enrichmentGraphQL을 사용하여 답변을 볼 수 있습니다.그물다음은 이러한 클래스를 위에서 생성한 클래스로 변환하는 방법에 대한 대형 코드 덤프입니다.
public class MetricsToTraceConverter
{
public Trace? CreateTrace(ExecutionResult result)
{
ApolloTrace? trace = result.Extensions != null && result.Extensions.ContainsKey("tracing") ? (ApolloTrace)result.Extensions["tracing"] : null;
var resolvers = trace?.Execution.Resolvers?
.OrderBy(x => string.Join(":", x.Path), new ConfigurationKeyComparer())
.ToArray();
var rootTrace = resolvers?.FirstOrDefault(x => x.Path.Count == 1);
if (rootTrace == null && result.Errors == null)
return null;
int resolverIndex = 1;
var rootErrors = result.Errors?.Where(x => x.Path != null && x.Path.Count() == 1).ToArray();
var rootNode = rootTrace != null && resolvers != null
? CreateNodes(rootTrace.Path, CreateNodeForResolver(rootTrace, rootErrors), resolvers, ref resolverIndex, GetSubErrors(rootTrace.Path, result.Errors?.ToArray()))
: new Trace.Node();
if (rootTrace == null && result.Errors != null)
{
foreach (var executionError in result.Errors)
rootNode.Errors.Add(CreateTraceError(executionError));
}
return new Trace
{
StartTime = trace?.StartTime ?? DateTime.Now,
EndTime = trace?.EndTime ?? DateTime.Now,
DurationNs = (ulong)(trace?.Duration ?? 0),
http = new Trace.Http { method = Trace.Http.Method.Post, StatusCode = result.Errors?.Any() == true ? (uint)HttpStatusCode.BadRequest : (uint)HttpStatusCode.OK },
Root = rootNode
};
}
private static Trace.Node CreateNodeForResolver(ApolloTrace.ResolverTrace resolver, ExecutionError[]? executionErrors)
{
var node = new Trace.Node
{
ResponseName = resolver.FieldName,
Type = resolver.ReturnType,
StartTime = (ulong)resolver.StartOffset,
EndTime = (ulong)(resolver.StartOffset + resolver.Duration),
ParentType = resolver.ParentType
};
if (executionErrors != null)
{
foreach (var executionError in executionErrors)
node.Errors.Add(CreateTraceError(executionError));
}
return node;
}
private static Trace.Error CreateTraceError(ExecutionError executionError)
{
var error = new Trace.Error
{
Json = JsonConvert.SerializeObject(executionError),
Message = executionError.Message
};
if (executionError.Locations != null)
error.Locations.AddRange(executionError.Locations.Select(x => new Trace.Location { Column = (uint)x.Column, Line = (uint)x.Line }));
return error;
}
private static ExecutionError[]? GetSubErrors(List<object> path, ExecutionError[]? errors)
{
return errors
?.Where(x => x.Path != null && x.Path.Count() > path.Count && x.Path.Take(path.Count).SequenceEqual(path))
.ToArray();
}
private static Trace.Node CreateNodes(List<object> path, Trace.Node node, ApolloTrace.ResolverTrace[] resolvers,
ref int resolverIndex, ExecutionError[]? executionErrors)
{
bool isArray = node.Type.StartsWith("[") && node.Type.TrimEnd('!').EndsWith("]");
if (isArray)
{
if (resolverIndex < resolvers.Length)
{
var resolver = resolvers[resolverIndex];
while (resolver.Path != null && resolver.Path.Count == path.Count + 2 && resolver.Path.Take(path.Count).SequenceEqual(path))
{
var index = (int)(resolver.Path[^2]);
var subPath = path.Concat(new object[] {index}).ToList();
var previousIndex = resolverIndex;
node.Childs.Add(CreateNodes(subPath,
new Trace.Node
{
Index = Convert.ToUInt32(index),
ParentType = node.Type,
Type = node.Type.TrimStart('[').TrimEnd('!').TrimEnd(']')
}, resolvers, ref resolverIndex, GetSubErrors(subPath, executionErrors)));
// Avoid infinite loop if the worst happens and we don't match any items for this index (HOW?!?!?)
if (resolverIndex == previousIndex)
resolverIndex++;
if (resolverIndex >= resolvers.Length)
break;
resolver = resolvers[resolverIndex];
}
}
}
else
{
if (resolverIndex < resolvers.Length)
{
var resolver = resolvers[resolverIndex];
while (resolver.Path != null && resolver.Path.Count == path.Count + 1 && resolver.Path.Take(path.Count).SequenceEqual(path))
{
var errors = executionErrors?.Where(x => x.Path.SequenceEqual(resolver.Path)).ToArray();
resolverIndex++;
node.Childs.Add(CreateNodes(resolver.Path, CreateNodeForResolver(resolver, errors), resolvers,
ref resolverIndex, GetSubErrors(resolver.Path, executionErrors)));
if (resolverIndex >= resolvers.Length)
break;
resolver = resolvers[resolverIndex];
}
}
}
return node;
}
}
그렇다면 이 모든 것은 무엇을 하고 있는 것일까?다음은 개요입니다.
public class MetricsToTraceConverter
{
public Trace? CreateTrace(ExecutionResult result)
{
ApolloTrace? trace = result.Extensions != null && result.Extensions.ContainsKey("tracing") ? (ApolloTrace)result.Extensions["tracing"] : null;
var resolvers = trace?.Execution.Resolvers?
.OrderBy(x => string.Join(":", x.Path), new ConfigurationKeyComparer())
.ToArray();
var rootTrace = resolvers?.FirstOrDefault(x => x.Path.Count == 1);
if (rootTrace == null && result.Errors == null)
return null;
int resolverIndex = 1;
var rootErrors = result.Errors?.Where(x => x.Path != null && x.Path.Count() == 1).ToArray();
var rootNode = rootTrace != null && resolvers != null
? CreateNodes(rootTrace.Path, CreateNodeForResolver(rootTrace, rootErrors), resolvers, ref resolverIndex, GetSubErrors(rootTrace.Path, result.Errors?.ToArray()))
: new Trace.Node();
if (rootTrace == null && result.Errors != null)
{
foreach (var executionError in result.Errors)
rootNode.Errors.Add(CreateTraceError(executionError));
}
return new Trace
{
StartTime = trace?.StartTime ?? DateTime.Now,
EndTime = trace?.EndTime ?? DateTime.Now,
DurationNs = (ulong)(trace?.Duration ?? 0),
http = new Trace.Http { method = Trace.Http.Method.Post, StatusCode = result.Errors?.Any() == true ? (uint)HttpStatusCode.BadRequest : (uint)HttpStatusCode.OK },
Root = rootNode
};
}
private static Trace.Node CreateNodeForResolver(ApolloTrace.ResolverTrace resolver, ExecutionError[]? executionErrors)
{
var node = new Trace.Node
{
ResponseName = resolver.FieldName,
Type = resolver.ReturnType,
StartTime = (ulong)resolver.StartOffset,
EndTime = (ulong)(resolver.StartOffset + resolver.Duration),
ParentType = resolver.ParentType
};
if (executionErrors != null)
{
foreach (var executionError in executionErrors)
node.Errors.Add(CreateTraceError(executionError));
}
return node;
}
private static Trace.Error CreateTraceError(ExecutionError executionError)
{
var error = new Trace.Error
{
Json = JsonConvert.SerializeObject(executionError),
Message = executionError.Message
};
if (executionError.Locations != null)
error.Locations.AddRange(executionError.Locations.Select(x => new Trace.Location { Column = (uint)x.Column, Line = (uint)x.Line }));
return error;
}
private static ExecutionError[]? GetSubErrors(List<object> path, ExecutionError[]? errors)
{
return errors
?.Where(x => x.Path != null && x.Path.Count() > path.Count && x.Path.Take(path.Count).SequenceEqual(path))
.ToArray();
}
private static Trace.Node CreateNodes(List<object> path, Trace.Node node, ApolloTrace.ResolverTrace[] resolvers,
ref int resolverIndex, ExecutionError[]? executionErrors)
{
bool isArray = node.Type.StartsWith("[") && node.Type.TrimEnd('!').EndsWith("]");
if (isArray)
{
if (resolverIndex < resolvers.Length)
{
var resolver = resolvers[resolverIndex];
while (resolver.Path != null && resolver.Path.Count == path.Count + 2 && resolver.Path.Take(path.Count).SequenceEqual(path))
{
var index = (int)(resolver.Path[^2]);
var subPath = path.Concat(new object[] {index}).ToList();
var previousIndex = resolverIndex;
node.Childs.Add(CreateNodes(subPath,
new Trace.Node
{
Index = Convert.ToUInt32(index),
ParentType = node.Type,
Type = node.Type.TrimStart('[').TrimEnd('!').TrimEnd(']')
}, resolvers, ref resolverIndex, GetSubErrors(subPath, executionErrors)));
// Avoid infinite loop if the worst happens and we don't match any items for this index (HOW?!?!?)
if (resolverIndex == previousIndex)
resolverIndex++;
if (resolverIndex >= resolvers.Length)
break;
resolver = resolvers[resolverIndex];
}
}
}
else
{
if (resolverIndex < resolvers.Length)
{
var resolver = resolvers[resolverIndex];
while (resolver.Path != null && resolver.Path.Count == path.Count + 1 && resolver.Path.Take(path.Count).SequenceEqual(path))
{
var errors = executionErrors?.Where(x => x.Path.SequenceEqual(resolver.Path)).ToArray();
resolverIndex++;
node.Childs.Add(CreateNodes(resolver.Path, CreateNodeForResolver(resolver, errors), resolvers,
ref resolverIndex, GetSubErrors(resolver.Path, executionErrors)));
if (resolverIndex >= resolvers.Length)
break;
resolver = resolvers[resolverIndex];
}
}
}
return node;
}
}
다음
에서 전체 보고서 클래스를 생성하여 Apollo Studio로 보내는 방법에 대해 알아봅니다.
Reference
이 문제에 관하여(Apollo Studio와 GraphQL을 통합합니다.네트워크 - 섹션 1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/mattjhosking/integrating-apollo-studio-with-graphql-for-net-part-1-4h5f
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Apollo Studio와 GraphQL을 통합합니다.네트워크 - 섹션 1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mattjhosking/integrating-apollo-studio-with-graphql-for-net-part-1-4h5f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)