[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2017년 3월 7일 화요일

[EP] SSAG 2nd Seminar 후기..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

페이스북의 Server Side Architecture Group(이하 SSAG)에서 2차 세미나를 해서 오랜만에 평일 저녁에 세미나에 참가했다. 나는 트위터를 위주로 사용하는 편이고 페이스북은 그 틈틈이 사용하는 편이라 여러 그룹에 가입되어 있지만, 그룹에 잘 들어가진 않는다. 사실 여러 그룹을 돌아보기 좋은 인터페이스도 아니긴 하지만 난 보통 글을 퍼블릭하게 쓰는 경향이 있어서 특정 그룹에 글은 잘 안 올리게 된다.(다른 사람은 반대인듯하다.) 어쨌든 SSAG는 꽤 큰 규모로 발전했고 얼마 전부터 세미나도 하기 시작했는데 이번이 그 2번째이다.

SSAG 시간표

MongoDB, 이렇게 쓰세요 - 한대희

온라인 데이팅 서비스인 이음에서 글로벌 데이팅 서비스로 만든 hey를 만들면서 MongDB를 적용했던 경험을 공유한 시간이었다. 내용은 발표자료에 잘 나와있으므로 굳이 따로 정리하지는 않겠다.(SSAG에서 동영상도 공유 예정인 걸로 알고 있다.)

나는 NoSQL 류에 관대한 입장을 가진 편이고 개인 프로젝트에서 MongoDB를 많이 쓰는 편인데 사실 이는 성능보다는(개인 프로젝트에서 스케일아웃 할 일이 있을 리가...) 개발 편의성 때문이다. 치밀한 계획하에 개발하지 않는 개인 프로젝트에서 MongoDB같은 스키마 없는 데이터베이스를 사용할 때의 편리함은 이루 말할 수 없다. 하지만 서비스로 나간다고 생각하면 글쎄... 쓰던 거니 고민은 하겠지만 덥석 쓰지는 않을 것 같다. 사실 이번 발표도 아주 깊은 내용이라기보다는 MongoDB는 사용 안 해봤지만 사용을 고려하는 입장에서는 미리 알면 도움될 부분이 상당히 많이 있다고 생각한다. 처음 발표한다고 하신 것 치고는 발표자료도 괜찮고 잘하신 듯...(나 처음 할 때 생각하면 덜덜덜)

이 발표의 결과에도 비슷한 얘기가 나오지만 조금 생각을 더하자면 난 기술을 좀 트랜디하게 쫓는 편이지만 새로운 기술을 사용할 때도 사람들이 너무 익숙한 기술의 결과와 수준을 비교한다고 생각하는 편이다. 오랫동안 잘 쓴 기술이 아무래도 더 잘 튜닝해서 쓰게 되어 있고 새로운 기술은 그러한 노하우가 없으므로 문서나 공유된 수준의 내용만 가지고 사용할 수밖에 없으므로 내부에 경험이 쌓이기 전까지는 아무래도 성능, 생산성 등의 저하를 경험할 수밖에 없는데 사람들은 종종 그런 단계를 한 번에 뛰어넘고 싶어하는 것 같다.(발표자분이 그렇다는 의미는 아니다.) 그리고 자주 나오듯이 NoSQL은 RDBMS를 대체하기 위해서 나온 것이 아니라 RDBMS가 커버하지 않는 부분을 커버하기 위해서 나온 것이다. 그럼에도 RDBMS의 대체재로 너무 1:1 비교개념으로 다가가면 자칫 망하기 쉽다.(DB에 대해 잘 아는 것이 아니므로 여기까지...)

발표 후 질답시간에 몇몇 질문에 대해서 청중에서 대신 답변을 하게 되면서 청중끼리 반박, 재반박이 이어지는 재미있는 토론이 있었다. 발표자 입장에서는 약간 곤혹스러운 상황일 수도 있겠지만 이렇게 자유로운 분위기(다양한 질문을 하고 그에 대해서 의견 있으면 서로 얘기하는...) 아주 좋다. 난 사실 발표 중에도 궁금한 게 있으면 손들고 묻고 그런 스터디스러운 발표를 더 좋아하는 편이다.

모바일 서비스 AWS 구축 경험 공유 - 김민태

NC Soft에서 소규모 팀으로 Chekit이라는 글로벌 서비스를 AWS에 구축한 경험을 공유한 발표로 AWS 기반 백앤드 구축에서 공유한 내용의 확장판이라고 볼 수 있다. 전에도 김민태님의 발표를 들은 적이 있는데 이번 발표도 세련된 발표자료에 능숙한 발표를 보여주셨다.(발표 중 여유로운 자세로 자연스러운 개그가 참 좋다.) 사용하는 개인 서버도 있고 AWS같은 확장성이나 유연성이 필요한 서비스를 만들어 본 적이없어서(혹은 AWS를 쓸 수 있는 환경이 아니거나) 주로 가상서버를 사용하고 있다. 그래서 AWS의 서비스들을 사용해 본적은 한 번도 없다. AWS의 인기가 워낙 좋으므로 관심은 상당히 있었지만 쓸 일이 없었던 탓에 관련 세미나도 별로 참여해보지 못했고 자세히 알지 못한다.

그런 면에서 나같이 AWS의 경험이 없는 사람에게는 아주 유용한 발표였다. AWS를 사용해 보지 않은 입장에서 서비스까지 구축하면서 고민한 부분과 삽질한 내용을 그대로 공유 받을 수 있었기 때문이다. 특히 AWS 서비스들의 가격(아무래도 가장 신경 쓰일 부분이니...)을 계산에 볼 수 있는 CloudMix의 서비스나 AWS에 구축할 때 사용할 수 있는 아키텍처 레퍼런스를 볼수 있는 페이지를 포함해서 Checkit이 어떤 아키텍처로 구성했는가 하는 내용은 나중에 AWS를 사용할 때 많이 도움이 될 것 같다.

AWS Cost Optimization: A journey to AWS cost optimization - 정유진

마지막에 미니 세션 비슷하게 AWS의 엔지니어분이 오셔서 AWS를 쓸 때 비용을 최적화해서 쓰려면 어떻게 해야 하는 가에 대해서 공유를 해주셨다. 나중에 AWS도 이제 한국에 들어와서 작년부터 세미나나 커뮤니티 등에서 이런 홍보를 많이 하는 듯한데 처음 듣는 나에겐 꽤 유용한 내용이었다. AWS를 사용할 때 가장 많이 하는 실수 중에 하나가 기존에 사내에서 사용하던 물리 서버와 같은 스펙으로 AWS를 같게 구축하는 것이다. 예시로 로드밸런서 1대, 웹서버 10대, 데이터베이스 서버 2대로 구성하면 한 달에 17,000불 정도가 나올 수 있다. 이렇게 구성한 경우 서버 대부분이 Idle 상태가 되는데 AWS의 비용은 "사용한 만큼 과금한다"는 개념이므로 이렇게 쓰는 것은 AWS를 제대로 쓰는 것이 아니다. EC2에는 "change instance type"라는 것이 있는데 이를 사용하면 5-10분 내에 원하는 대로 서버의 성능을 변경할 수 있다.

그래서 AWS를 사용할 때는 불필요하게 좋은 성능을 사용하지 않아야 한다. m3.xlarge를 m1.small로 바꾸고 db.m2.xlarge를 db.m1.small로 바꾸면 월 8,000불 정도로 줄일 수 있다. 여기에 EBS를 PIOPS에서 Standard 50GB로 바꾸고 RDS 볼륨을 1TB PIOPS에서 Standard 50GB로 바꾸면 월 700불까지로 내릴 수 있다. 보통의 서비스는 인바운드보다 아웃바운드 트래픽이 더 많은데 아마존은 아웃바운드 트래픽에 대해서만 과금을 하므로 AWS의 서비스를 적절히 이용하면 비용을 줄일 수 있다. 그리고 트래픽에 맞춰서 서버사양을 늘리는 오토 스케일링을 될 수 있으면 사용해라. 서비스 대부분은 시간별로 트래픽이 다른데 계속 좋은 사양을 유지하지 않고 트래픽 증가에 따라 자동으로 변경되게 설정하면 비용을 월 450불 정도까지 낮출 수 있다. 마지막으로 Reserved Instances를 사용하는 것이 좋다. Reserved Instances는 장기간(1년, 3년 등)의 비용을 미리 내면 할인을 해주는 과금체계인데 3개월 이상 쓰게 되면 웬만하면 Reserved Instances를 사용하는 것이 더 낮다. 서버의 최소 트래픽에 맞춘 사양 정도를 Reserved Instances로 사용하고 나머지는 오토스케일링으로 확장해서 쓰면 비용을 더 낮출 수 있다.

맺음말

오랜만에 평일날 참가한 세미나였는데 관심 주제이기도 했지만 발표도 괜찮고 사람들 호응도 좋은 즐거운 세미나였다. 더군다나 쟁쟁한 분들이 엄청나게 모여든.... 너무 인기가 좋아서 장소가 좀 협소하게 느껴진 게 아쉬웠다면 약간 아쉬웠고 평일날 저녁 8시에 시작한 세미나라 사람들과 교류를 나눌 시간이 좀 부족했지만 이건 뭐 어쩔 수 없는 부분이니..

2017년 3월 2일 목요일

[Spring 레퍼런스] 23장 JMX #2..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.

23.4 빈의 ObjectName 제어

내부적으로 MBeanExporter는 ObjectNamingStrategy 구현체에 등록하는 각 빈의 ObjectName을 가져오는 전략을 위임한다. 기본 구현체 KeyNamingStrategy는 beans Map의 키를 ObjectName로 사용한다. 그리고 KeyNamingStrategy는 beans Map의 키를 Properties의 항목에 매핑해서 ObjectName을 처리할 수 있다. KeyNamingStrategy외에도 스프링은 두 가지 ObjectNamingStrategy 구현체를 더 제공하고 있다. IdentityNamingStrategy는 빈의 JVM 아이디(identity)에 기반해서 ObjectName을 만들고 MetadataNamingStrategy은 소스수준의 메타데이터를 사용해서 ObjectName를 얻는다.

23.4.1 Properties에서 ObjectName 읽어오기

자신만의 KeyNamingStrategy 인스턴스를 설정해서 빈의 키를 사용하는 대신 Properties 인스턴스에서 ObjectName을 읽어오도록 구성할 수 있다. KeyNamingStrategy는 빈 키에 대응되는 키를 가진 Properties의 요소를 찾으려고 할 것이다. 일치하는 항목이 없거나 Properties 인스턴스가 null이라면 빈 키 자체를 사용한다. 아래 코드는 KeyNamingStrategy의 설정 예시를 보여준다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="testBean" value-ref="testBean"/>
      </map>
    </property>
    <property name="namingStrategy" ref="namingStrategy"/>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <bean id="namingStrategy" class="org.springframework.jmx.export.naming.KeyNamingStrategy">
    <property name="mappings">
      <props>
        <prop key="testBean">bean:name=testBean1</prop>
      </props>
    </property>
    <property name="mappingLocations">
      <value>names1.properties,names2.properties</value>
    </property>
  </bean>
</beans>

여기서 KeyNamingStrategy 인스턴스는 매핑 프로퍼티와 매핑 프로퍼티에 정의된 경로에 있는 프로퍼티 파일로 정의된 Properties 인스턴스와 합쳐진 Properties 인스턴스로 구성된다. 이 구성에서 testBean 빈은 ObjectName bean:name=testBean1을 받을 것이다. 이는 이 값이 Properties 인스턴스에서 빈 키에 대응되는 키를 가진 항목이기 때문이다. Properties 인스턴스에 항목이 없다면 빈 키의 이름을 ObjectName으로 사용한다.


23.4.2 MetadataNamingStrategy의 사용

MetadataNamingStrategy는 ObjectName을 사용하기 위해 각 빈의 ManagedResource속성의 objectName 프로퍼티를 사용한다. 아래 코드는 MetadataNamingStrategy의 설정 예시를 보여준다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="testBean" value-ref="testBean"/>
      </map>
    </property>
    <property name="namingStrategy" ref="namingStrategy"/>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
    <property name="attributeSource" ref="attributeSource"/>
  </bean>

  <bean id="attributeSource" class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource"/>
</beans>

ManagedResource 속성에 제공된 objectName이 없다면 ObjectName은 [fully-qualified-package-name]:type=[short-classname],name=[bean-name]의 형식으로 생성될 것이다. 예를 들어 다음 빈에 생성된 ObjectName은 com.foo:type=MyClass,name=myBean가 될 것이다.

1
2
3
Xml

<bean id="myBean" class="com.foo.MyClass"/>


23.4.3 <context:mbean-export/> 요소

Java 5 이상을 사용함다면 MBeanExporter의 편리한 하위 클래스 AnnotationMBeanExporter를 사용할 수 있다. 이 하위 클래스의 인스턴스를 정의하면 표준 자바 어노테이션 기반의 메타데이터를 항상 사용할 것이므로 (자동탐지도 항상 활성화된다) namingStrategy, assembler, attributeSource 설정이 더이상 필요없다. 게다가 스프링의 'context' 네임스페이스가 지원하는 더 간단한 분법도 가능하다. MBeanExporter 빈을 정의하는 대신 다음의 요소를 지정하면 된다.

1
2
3
Xml

<context:mbean-export/>

필요하다면 특정 MBean 서버에 대한 참조를 제공할 수도 있고 defaultDomain 속성 (AnnotationMBeanExporter의 프로퍼티)은 생성된 Mbean ObjectNames의 도메인의 대체값을 받는다. 이전 MetadataNamingStrategy 섹션에서 설명한대로 이는 정규화된 패키지명에 사용한다.

1
2
3
Xml

<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>


Note
빈(bean) 클래스에서 JMX 어노테이션의 자동탐지와 함께 인터페이스 기반의 AOP 프록시는 사용하지 말아라. 인터페이스 기반의 프록시는 대상 클래스를 '숨기고' JMX가 관리하는 리소스 어노테이션도 숨긴다. 그러므로 이러한 경우에는 대상클래스(target-class) 프록시를 사용해야 한다. 나 등의 'proxy-target-class' 플래그를 설정하더라도 JMX 빈은 구동시 이를 경고없이 무시할 것이다.


23.5 JSR-160 커넥터

리모트 접근을 위해서 스프링 JMX 모듈은 서버측과 클라이언트측 커넥터를 생성하는 org.springframework.jmx.support 패키지에서 FactoryBean의 두가지 구현체를 제공한다.

23.5.1 서버측 커넥터

스프링 JMX가 JSR-160 JMXConnectorServer를 생성해서 구동하고 노출하게 하려면 다음 설정을 사용해라.

1
2
3
Xml

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>

기본적으로 ConnectorServerFactoryBean는 JMXConnectorServer을 "service:jmx:jmxmp://localhost:9875"에 바인딩해서 생성한다. 그러므로 serverConnector 빈은 MBeanServer를 localhost의 JMXMP 프로토콜과 9875 포트로 노출한다. JMXMP 프로토콜은 JSR-160 명세에 선택사항으로 나와 있다. 현재로써는 주요 오픈소스 JMX 구현체인 MX4J와 J2SE 5.0이 제공하는 구현체가 JMXMP를 지원하지 않는다.
다른 URL을 지정해서 JMXConnectorServer 자체를 MBeanServer에 등록하려면 serviceUrl과 ObjectName 프로퍼티를 각각 사용해라.

1
2
3
4
5
6
Xml

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
  <property name="objectName" value="connector:name=rmi"/>
  <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/>
</bean>

ObjectName 프로퍼티를 설정하면 스프링이 해당 ObjectName의 MBeanServer에 커넥터를 자동으로 등록할 것이다. 다음 예제는 JMXConnector 생성시 ConnectorServerFactoryBean에 전달할 수 있는 전체 파라미터를 보여주고 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Xml

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
  <property name="objectName" value="connector:name=iiop"/>
  <property name="serviceUrl" value="service:jmx:iiop://localhost/jndi/iiop://localhost:900/myconnector"/>
  <property name="threaded" value="true"/>
  <property name="daemon" value="true"/>
  <property name="environment">
    <map>
      <entry key="someKey" value="someValue"/>
    </map>
  </property>
</bean>

RMI에 기반한 커넥터를 사용한다면 이름 등록과정이 완료된 후 시작되도록 검색 서비스(tnameserv나 rmiregistry)가 필요하다. RMI로 원격 서비스를 내보내도록 스프링을 사용하고 있다면 스프링은 이미 RMI 레지스트리를 생성했을 것이다. RIM로 원격서비스를 내보내도록 하고 있지 않다면 다음의 설정을 사용해서 간단하게 레지스트리를 시작할 수 있다.

1
2
3
4
5
Xml

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
  <property name="port" value="1099"/>
</bean>


23.5.2 클라이언트측 커넥터

원격 JSR-160를 사용하는 MBeanServer에 MBeanServerConnection를 생성하려면 다음과 같이 MBeanServerConnectionFactoryBean를 사용해라.

1
2
3
4
5
Xml

<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
  <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>


23.5.3 Burlap/Hessian/SOAP에서의 JMX

JSR-160은 클라이언트와 서버간의 통신을 수행하는 방법에 확장포인트를 허용하고 있다. 앞의 예제는 JSR-160 명세에 명시된 의무적인 RMI 기반의 구현체(IIOP와 JRMP)와 (선택적인) JMXMP를 사용하고 있다. 다른 프로바이더나 JMX 구현체 (MX4J와 같은)를 사용해서 HTTP나 SSL등을 사용하는 SOAP, Hessian, Burlap같은 프로토콜의 장점을 취할 수 있다.

1
2
3
4
5
6
Xml

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
  <property name="objectName" value="connector:name=burlap"/>
  <property name="serviceUrl" value="service:jmx:burlap://localhost:9874"/>
</bean>

앞의 예제에서는 MX4J 3.0.0을 사용했다. 자세한 내용은 MX4J 공식 문서를 참고해라.


23.6 프록시를 통한 MBean 접근

스프링 JMX에서는 로컬이나 원격 MBeanServer에 등록된 MBean 호출을 리라우팅하는 프록시를 생성할 수 있다. 이러한 프록시는 MBean과 상호작용할 수 있는 표준 자바 인터페이스를 제공한다. 다음 코드는 로컬 MBeanServer에서 동작 중인 MBean에 대한 프록시를 구성하는 방법을 보여준다.

1
2
3
4
5
6
Xml

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
  <property name="objectName" value="bean:name=testBean"/>
  <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
</bean>

ObjectName: bean:name=testBean으로 등록된 MBean에 프록시가 생성된 것을 볼 수 있다. 프록시가 구현하는 인터페이스들은 proxyInterfaces 프로퍼티로 제어하고 이 인터페이스들의 메서드와 프로퍼티를 MBean의 동작과 속성으로 매핑하는 규칙은 InterfaceBasedMBeanInfoAssembler에서 사용한 규칙과 동일하다. MBeanProxyFactoryBean은 MBeanServerConnection로 접근할 수 있는 모든 MBean에 대해서 프록시를 생성할 수 있다. 기본적으로 로컬 MBeanServer를 사용하지만 이를 오버라이드해서 원격 MBean을 가리키는 프록시에 대한 원격 MBeanServer를 가리키는 MBeanServerConnection를 제공할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Xml

<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
  <property name="serviceUrl" value="service:jmx:rmi://remotehost:9875"/>
</bean>

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
  <property name="objectName" value="bean:name=testBean"/>
  <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
  <property name="server" ref="clientConnector"/>
</bean>

위 예제에서 MBeanServerConnectionFactoryBean를 사용하는 원격 머신을 가리키는 MBeanServerConnection를 생성한 것을 볼 수 있다. server 프로퍼티로 이 MBeanServerConnection을 MBeanProxyFactoryBean에 전달한다. 생성한 프록시는 이 MBeanServerConnection를 사용해서 모든 호출을 MBeanServer로 보낼 것이다.


23.7 알림(Notification)

스프링의 JMX는 JMX 알림도 광범위하게 지원하고 있다.

23.7.1 알림에 대한 리스너 등록

스프링의 JMX는 다수의 MBean(이는 스프링의 MBeanExporter가 내보낸 MBean과 다른 메카니즘으로 등록된 MBean을 포함한다.)을 가진 다수의 NotificationListeners을 아주 쉽게 등록할 수 있도록 지원한다. 예제를 통해서 대상 MBean의 속성이 변경될 때마다 알림을 받는(Notification으로) 시나리오를 생각해 보자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Java

package com.example;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

public class ConsoleLoggingNotificationListener implements NotificationListener, NotificationFilter {

  public void handleNotification(Notification notification, Object handback) {
    System.out.println(notification);
    System.out.println(handback);
  }

  public boolean isNotificationEnabled(Notification notification) {
    return AttributeChangeNotification.class.isAssignableFrom(notification.getClass());
  }
}


Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
    <property name="notificationListenerMappings">
      <map>
        <entry key="bean:name=testBean1">
          <bean class="com.example.ConsoleLoggingNotificationListener"/>
        </entry>
      </map>
    </property>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>
</beans>

위 설정에서 대상 MBean(bean:name=testBean1)이 JMX Notification를 브로드캐스트할 때마다 notificationListenerMappings 프로퍼티에 리스너로 등록한 ConsoleLoggingNotificationListener 빈이 알림을 받을 것이다. ConsoleLoggingNotificationListener 빈은 Notification의 응답으로 적절한 모든 액션을 받을 수 있다.
내보낸 빈과 리스너간의 연결로 빈 이름을 직접 사용할 수도 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
    <property name="notificationListenerMappings">
      <map>
        <entry key="testBean">
          <bean class="com.example.ConsoleLoggingNotificationListener"/>
        </entry>
      </map>
    </property>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>
</beans>

MBeanExporter가 내보내는 모든 빈에 하나의 NotificationListener 인스턴스를 등록하고 싶다면 notificationListenerMappings 프로퍼티 맵의 엔트리에 키로 '*' (따옴표없이)를 와일드카드로 사용할 수 있다. 예를 들면 다음과 같다.

1
2
3
4
5
6
7
8
9
Xml

<property name="notificationListenerMappings">
  <map>
    <entry key="*">
      <bean class="com.example.ConsoleLoggingNotificationListener"/>
    </entry>
  </map>
</property>

반대로 해야 한다면(즉, MBean에 다수의 리스너를 등록한다면) notificationListeners 리스트 프로퍼티(그리고 notificationListenerMappings 프로퍼티에 대한 설정에)를 대신 사용해야 한다. 이 때 단일 MBean의 NotificationListener를 설정하는 대신 NotificationListenerBean 인스턴스를 설정해라. NotificationListenerBean은 NotificationListener와 ObjectName(또는 ObjectNames)을 은닉화하고 이는 MBeanServer에 등록된다. NotificationListenerBean은 NotificationFilter와 고급 JMX 알림 시나리오에서 사용할 수 있는 임의의 handback 객체같은 다수의 프로퍼티들도 은닉화한다. NotificationListenerBean 인스턴스를 사용할 때의 설정은 이전에 본 설정과 크게 다르지 않다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
    <property name="notificationListeners">
      <list>
        <bean class="org.springframework.jmx.export.NotificationListenerBean">
          <constructor-arg>
            <bean class="com.example.ConsoleLoggingNotificationListener"/>
          </constructor-arg>
          <property name="mappedObjectNames">
            <list>
              <value>bean:name=testBean1</value>
            </list>
          </property>
        </bean>
      </list>
    </property>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>
</beans>

위의 예제는 첫 알림 예제와 같다. Notification이 발생할 때마다 handback 객체가 주어지고 해당 NotificationFilter로 관련없는 Notifications을 필터링하기를 원한다고 해보자. (handback 객체가 무엇이고 NotificationFilter가 무엇인지에 대해서는 JMX 명세서(1.2)의 'The JMX Notification Model' 부분을 참고하기 바란다.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Xml

<beans>
  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean1"/>
        <entry key="bean:name=testBean2" value-ref="testBean2"/>
      </map>
    </property>
    <property name="notificationListeners">
      <list>
        <bean class="org.springframework.jmx.export.NotificationListenerBean">
          <constructor-arg ref="customerNotificationListener"/>
          <property name="mappedObjectNames">
            <list>
              <!-- 두 별개의 MBean에서 받은 알림을 처리한다 -->
              <value>bean:name=testBean1</value>
              <value>bean:name=testBean2</value>
            </list>
          </property>
          <property name="handback">
            <bean class="java.lang.String">
              <constructor-arg value="This could be anything..."/>
            </bean>
          </property>
          <property name="notificationFilter" ref="customerNotificationListener"/>
        </bean>
      </list>
    </property>
  </bean>

  
  <bean id="customerNotificationListener" class="com.example.ConsoleLoggingNotificationListener"/>

  <bean id="testBean1" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <bean id="testBean2" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="ANOTHER TEST"/>
    <property name="age" value="200"/>
  </bean>
</beans>


23.7.2 알림 발행하기

스프링은 Notifications을 받기 위한 등록뿐 아니라 Notifications을 보내기위한 등록도 지원한다.


Note
이 부분은 MBeanExporter를 통해 MBean으로 노출된 스프링이 관리하는 빈에 대해서만 관련된 부분이다. 이미 존재하거나 사용자가 정의한 모든 MBean은 표준 JMX API를 사용해서 알림을 발행해야 한다.

스프링의 JMX 알림 발행 지원에서 핵심 인터페이스는 NotificationPublisher 인터페이스다. (org.springframework.jmx.export.notification 패키지에 정의되어 있다.) MBeanExporter 인스턴스를 통해서 MBean으로 내보내질 모든 빈은 NotificationPublisher 인스턴스에 접근할 수 있도록 NotificationPublisherAware 인터페이스를 구현할 수 있다. NotificationPublisherAware 인터페이스는 해당 빈이 Notifications을 발행하는데 사용할 수 있는 간단한 setter 메서드로 구현한 빈에 NotificationPublisher 인스턴스를 제공한다. NotificationPublisher 클래스의 Javadoc에 나온대로 NotificationPublisher 메카니즘으로 이벤트를 발행하면서 관리되고 있는 빈은 어떤 알림 리스너 등에 대해서도 상태관리를 책임지지 않는다. 스프링의 JMX 지원은 JMX 인프라스트럭처와 관련된 모든 이슈의 처리를 담당한다. 애플리케이션 개발자가 해야하는 일은 NotificationPublisherAware 인터페이스를 구현하고 제공된 NotificationPublisher 인스턴스를 사용해서 이벤트를 발행하는 것 뿐이다. 관리되는 빈이 MBeanServer에 등록된 후에 NotificationPublisher가 설정된다는 점에 주의해라.

NotificationPublisher 인스턴스의 사용은 아주 직관적이다. 그냥 Notification 인스턴스(또는 Notification의 적절한 하위클래스의 인스턴스)를 만들고 발행할 이벤트와 관련된 데이터로 알림을 만든 후 NotificationPublisher 인스턴스의 sendNotification(Notification)를 호출해서 Notification을 전달하면 된다. 아래의 간단한 예제를 보자. 이 시나리오에서 내보내진 JmxTestBean 인스턴스는 add(int, int)가 호출될 때마다 NotificationEvent를 발행할 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Java

package org.springframework.jmx;

import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.export.notification.NotificationPublisher;
import javax.management.Notification;

public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware {

  private String name;
  private int age;
  private boolean isSuperman;
  private NotificationPublisher publisher;

  // 다른 getter와 setter는 생략했다

  public int add(int x, int y) {
    int answer = x + y;
    this.publisher.sendNotification(new Notification("add", this, 0));
    return answer;
  }

  public void dontExposeMe() {
    throw new RuntimeException();
  }

  public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
    this.publisher = notificationPublisher;
  }
}

NotificationPublisher 인터페이스와 관련된 부분이 모두 잘 동작한다는 점이 스프링의 JMX 지원을 좋은 점중 하나다. 하지만 클래스가 스프링과 JMX에 모두 의존성을 갖게 된다는 단점이 있다. 언제나 그렇듯이 이에 대한 조언은 현실적인 조언 뿐이다. NotificationPublisher이 제공하는 기능이 필요하고 스프링과 JMX에 대한 의존성을 받아들일 수 있다면 사용해도 좋다.


23.8 추가 자료

이 섹션에서는 JMX에 대한 추가자료와 관련된 링크를 제공한다.