임베디드 프로브
프로브의 대상이 되는 소프트웨어 컴포넌트의 소스 코드를 제어할 수 있는 경우, 주입된 프로브 대신 임베디드 프로브를 작성해야 합니다.
주입된 프로브를 작성할 때 초기 노력의 대부분은 인터셉트된 메소드를 지정하고 핸들러 메소드의 매개변수로 주입된 객체를 선택하는 데 들어갑니다. 임베디드 프로브를 사용하면 모니터링된 메소드에서 임베디드 프로브 API를 직접 호출할 수 있기 때문에 이러한 작업이 필요하지 않습니다. 임베디드 프로브의 또 다른 장점은 배포가 자동이라는 것입니다. 프로브는 소프트웨어와 함께 제공되며 애플리케이션이 프로파일될 때 JProfiler UI에 나타납니다. 제공해야 하는 유일한 종속성은 주로 프로파일링 에이전트의 훅으로 작동하는 빈 메소드 본문으로 구성된 Apache 2.0 라이선스 하의 작은 JAR 파일입니다.
개발 환경
개발 환경은 주입된 프로브와 동일하지만, 아티팩트 이름이 jprofiler-probe-embedded
이고 JAR 파일을 별도의 프로젝트에서 프로브를 개발하는 대신
애플리케이션과 함께 제공한다는 점이 다릅니다. 소프트웨어 컴포넌트에 임베디드 프로브를 추가하는 데 필요한 프로브 API는 단일 JAR 아티팩트에 포함되어 있습니다. javadoc에서 API를 탐색할 때
com.jprofiler.api.probe.embedded
패키지 개요부터 시작하십시오.
주입된 프로브와 마찬가지로 임베디드 프로브에도 두 가지 예제가 있습니다. api/samples/simple-embedded-probe
에는 임베디드 프로브 작성을 시작하는 데 도움이
되는 예제가 있습니다. 해당 디렉토리에서 ../gradlew
를 실행하여 컴파일하고 실행하며, 실행 환경을 이해하기 위해 gradle 빌드 파일
build.gradle
를 연구하십시오. 제어 객체를 포함한 더 많은 기능을 보려면 api/samples/advanced-embedded-probe
의 예제로
이동하십시오.
페이로드 프로브
주입된 프로브와 유사하게, 여전히 구성 목적으로 프로브 클래스가 필요합니다. 프로브 클래스는 페이로드를 수집하는지 호출 트리를 분할하는지에 따라
com.jprofiler.api.probe.embedded.PayloadProbe
또는
com.jprofiler.api.probe.embedded.SplitProbe
를 확장해야 합니다. 주입된 프로브 API에서는 핸들러 메소드에 대한 페이로드 수집 및 분할에 대해
다른 주석을 사용합니다. 반면에 임베디드 프로브 API에는 핸들러 메소드가 없으며 이 구성을 프로브 클래스 자체로 이동해야 합니다.
public class FooPayloadProbe extends PayloadProbe { @Override public String getName() { return "Foo queries"; } @Override public String getDescription() { return "Records foo queries"; } }
주입된 프로브가 구성을 위해 주석을 사용하는 반면, 임베디드 프로브는 프로브의 기본 클래스에서 메소드를 재정의하여 구성합니다. 페이로드 프로브의 경우 유일한 추상 메소드는
getName()
이며, 다른 모든 메소드는 필요에 따라 재정의할 수 있는 기본 구현을 가지고 있습니다. 예를 들어, 오버헤드를 줄이기 위해 이벤트 뷰를 비활성화하려면
isEvents()
를 재정의하여 false
를 반환할 수 있습니다.
페이로드를 수집하고 관련된 시간을 측정하기 위해 Payload.enter()
와 Payload.exit()
호출 쌍을 사용합니다.
public void measuredCall(String query) { Payload.enter(FooPayloadProbe.class); try { performWork(); } finally { Payload.exit(query); } }
Payload.enter()
호출은 프로브 클래스를 인수로 받으므로 프로파일링 에이전트가 호출의 대상 프로브를 알 수 있으며, Payload.exit()
호출은 자동으로 동일한 프로브와 연결되며 페이로드 문자열을 인수로 받습니다. 종료 호출을 놓치면 호출 트리가 깨질 수 있으므로 항상 try 블록의 finally 절에서 수행해야 합니다.
측정된 코드 블록이 값을 생성하지 않는 경우, 페이로드 문자열과 Runnable
을 인수로 받는 Payload.execute
메소드를 대신 호출할 수
있습니다. Java 8+에서는 람다 또는 메소드 참조를 사용하여 이 메소드 호출을 매우 간결하게 만들 수 있습니다.
public void measuredCall(String query) { Payload.execute(FooPayloadProbe.class, query, this::performWork); }
이 경우 페이로드 문자열은 사전에 알려져 있어야 합니다. Callable
을 받는 execute
의 버전도 있습니다.
public QueryResult measuredCall(String query) throws Exception { return Payload.execute(PayloadProbe.class, query, () -> query.execute()); }
Callable
을 받는 시그니처의 문제는 Callable.call()
이 체크된 Exception
을 던지므로 이를 잡거나
포함하는 메소드에 선언해야 한다는 것입니다.
제어 객체
페이로드 프로브는 Payload
클래스의 적절한 메소드를 호출하여 제어 객체를 열고 닫을 수 있습니다. 제어 객체는 제어 객체와 사용자 정의 이벤트 유형을 받는
Payload.enter()
또는 Payload.execute()
메소드의 버전으로 프로브 이벤트와 연결됩니다.
public void measuredCall(String query, Connection connection) { Payload.enter(FooPayloadProbe.class, connection, MyEventTypes.QUERY); try { performWork(); } finally { Payload.exit(query); } }
제어 객체 뷰는 프로브 구성에서 명시적으로 활성화해야 하며, 사용자 정의 이벤트 유형은 프로브 클래스에서도 등록해야 합니다.
public class FooPayloadProbe extends PayloadProbe { @Override public String getName() { return "Foo queries"; } @Override public String getDescription() { return "Records foo queries"; } @Override public boolean isControlObjects() { return true; } @Override public Class<? extends Enum> getCustomTypes() { return Connection.class; } }
제어 객체를 명시적으로 열고 닫지 않는 경우, 프로브 클래스는 모든 제어 객체에 대한 표시 이름을 해결하기 위해 getControlObjectName
을 재정의해야 합니다.
분할 프로브
분할 프로브 기본 클래스에는 추상 메소드가 없으므로 프로브 뷰를 추가하지 않고도 호출 트리를 분할하는 데 사용할 수 있습니다. 이 경우 최소한의 프로브 정의는 다음과 같습니다.
public class FooSplitProbe extends SplitProbe {}
분할 프로브의 중요한 구성 중 하나는 재진입 가능 여부입니다. 기본적으로 최상위 호출만 분할됩니다. 재귀 호출도 분할하려면 isReentrant()
를 재정의하여
true
를 반환하도록 합니다. 분할 프로브는 또한 프로브 뷰를 생성하고 분할 문자열을 페이로드로 게시할 수 있으며, 프로브 클래스에서
isPayloads()
를 재정의하여 true
를 반환하도록 할 수 있습니다.
분할을 수행하려면 Split.enter()
와 Split.exit()
호출 쌍을 만듭니다.
public void splitMethod(String parameter) { Split.enter(FooSplitProbe.class, parameter); try { performWork(parameter); } finally { Split.exit(); } }
페이로드 수집과 달리, 분할 문자열은 프로브 클래스와 함께 Split.enter()
메소드에 전달되어야 합니다. 다시 말하지만, Split.exit()
가
신뢰할 수 있게 호출되는 것이 중요하므로, 이는 try 블록의 finally 절에 있어야 합니다. Split
는 Runnable
및
Callable
인수를 사용하여 단일 호출로 분할을 수행하는 execute()
메소드도 제공합니다.
텔레메트리
임베디드 프로브에 대해 텔레메트리를 게시하는 것은 특히 편리합니다. 동일한 클래스패스에 있기 때문에 애플리케이션의 모든 정적 메소드에 직접 접근할 수 있습니다. 주입된 프로브와 마찬가지로, 프로브 구성
클래스의 정적 공용 메소드에 @Telemetry
를 주석으로 달고 숫자 값을 반환합니다. 자세한 내용은 프로브 개념
장을 참조하십시오. 임베디드 및 주입된 프로브 API의 @Telemetry
주석은 동일하며, 단지 다른 패키지에 있습니다.
임베디드 및 주입된 프로브 API 간의 또 다른 병렬 기능은 ThreadState
클래스를 사용하여 스레드 상태를 수정할 수 있는 기능입니다. 이 클래스는 두 API에 모두
존재하며 다른 패키지에 있습니다.
배포
JProfiler UI로 프로파일링할 때 임베디드 프로브를 활성화하기 위해 특별한 단계는 필요하지 않습니다. 그러나 Payload
또는 Split
에
대한 첫 번째 호출이 이루어질 때만 프로브가 등록됩니다. 그 시점에만 JProfiler에서 관련된 프로브 뷰가 생성됩니다. 기본 제공 및 주입된 프로브의 경우처럼 처음부터 프로브 뷰가 보이기를 원한다면,
다음을 호출할 수 있습니다.
PayloadProbe.register(FooPayloadProbe.class);
페이로드 프로브의 경우
SplitProbe.register(FooSplitProbe.class);
분할 프로브의 경우.
오버헤드를 최소화하기 위해 명령줄 스위치로 제어되는 조건부로 Payload
및 Split
메소드를 호출할지 여부를 고려할 수 있습니다. 그러나 메소드
본문이 비어 있기 때문에 일반적으로 필요하지 않습니다. 프로파일링 에이전트가 attach되지 않은 경우, 페이로드 문자열의 생성 외에는 오버헤드가 발생하지 않습니다. 프로브 이벤트는 미세한 규모로
생성되지 않아야 하므로 비교적 드물게 생성되며, 페이로드 문자열을 생성하는 것은 비교적 사소한 작업이어야 합니다.
컨테이너의 또 다른 우려 사항은 클래스 경로에 외부 종속성을 노출하고 싶지 않을 수 있다는 것입니다. 컨테이너의 사용자가 임베디드 프로브 API를 사용할 수도 있으며, 이는 충돌을 초래할 수 있습니다. 이 경우, 임베디드 프로브 API를 자체 패키지로 셰이딩할 수 있습니다. JProfiler는 여전히 셰이딩된 패키지를 인식하고 API 클래스를 올바르게 계측합니다. 빌드 타임 셰이딩이 실용적이지 않은 경우, 소스 아카이브를 추출하여 클래스를 프로젝트의 일부로 만들 수 있습니다.