application 간 전환 command + tab (좌 -> 우) / command + shift + tab (우 -> 좌)
※ app 간 전환화면에서 이전 선택으로 가는 방법은 윈도우와 동일하게 shift 를 추가로 누르고 tab 키를 누르면 이전 선택(우->좌) 으로 포커스가 이동한다.
※ 또 다른 방법은 전환화면 상에서 cmd + ' 를 누르면 위와 같이 동작한다.
한 application 내 multiple window 간 전환 command + ` (좌 -> 우) / command + shift + ` (우 -> 좌)
※ app 간 전환 (cmd + tab) 기능은 windows os 처럼 화면위에 switch 되는 app 의 아이콘이 나오기 때문에 전환 하려는 app을 명시적으로 선택이 가능하지만
app 내 창 전환은 전환 하려는 창들이 화면에 표시되지 않고 순차적으로 창들이 switch 되기 때문에 명시적으로 전환이 어렵다.
이럴 경우 app 간 전환 (cmd + tab ) 으로 전환 하려는 창을 선택 후 tab 키에선 손을 때고 cmd 키만 누른 상태에서 숫자 1 키를 누르면 expose 기능이 실행 되면서
해당 app 에 열려 있는 창들이 모두 보이면서 방향키로 전환하려는 창을 선택가능하게 된다.
- expose 기능 실행 방법 1 : cmd + tab 전환 화면 상에서 expose 기능을 보려는 app 에 focus 를 맞춘후 숫자 1 키
- expose 기능 실행 방법 2 : f10 현재 실행되는 app 의 expose 실행
(또는 shift+f10 or ctrl+f10 - mission control 설정에 따라 다름)
단축키 설정 : 시스템환경설정 -> 키보드 -> 단축키 -> Mission Control -> 응용 프로그램 윈도우
2017년 2월 26일 일요일
2017년 2월 5일 일요일
zookeeper node watcher 와 callback
Watcher Type
operation
| create |
delete
|
child create
|
child delete
|
change
|
exists
|
NodeCreate
|
NodeDelete
|
NodeDataChanged
|
||
getData
|
NodeDelete
|
NodeDataChanged
|
|||
getChildren
|
NodeDelete
|
NodeChildrenChanged
|
NodeChildrenChanged
|
NODE_CREATED : 노드가 생성 됨을 감지
NODE_DELETE : 노드가 삭제 됨을 감지
NODE_DATA_CHANGED : 노드의 데이터가 변경됨을 감지
NODE_CHILDREN_CHANGED : 자식 노드가 변경 됨을 감지
Node 생성 종류
[zookeeper.create(nodepath, nodedata, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode, Callback, Object)]
CreateMode.PERSISTENT 영속성
CreateMode.EPHEMERAL 해당 세션이 살아 있을 경우에만 지속
[Executor]
public class Executor implements Runnable, AsyncCallback.ChildrenCallback {
ZooKeeper zk;
public Executor() throws IOException {
zk = new ZooKeeper("127.0.0.1:2181", 3000, null);
// default watcher 등록
Watcher w = new NodeMonitor(zk, TestNodeManager.getInstacne());
zk.register(w);
try {
/*
1. a node 의 children node 변경 감지 (a node delete, child delete, child create)
- zookeeper connection 생성시 등록한 default watcher or zk.register(w) watcher -
2. zk.getChildren 실행되면
- watcher 가 변경 감지하여 실행하는 것은 아님 변경감지하면 watcher 의 process method 실행됨 -
실행결과(children node List)를 등록한 callback -w callback (processResult)- 으로 후 처리
*/
// zk.getChildren("/a", true, (AsyncCallback.ChildrenCallback) w, null);
// 하위 노드에 watcher 를 모두 등록하려면 현재 class 에서 등록하는게 더 직관적
zk.getChildren("/a", true, this, null);
// 최초 시작시 a node 변경 감지를 위한 설정 - create, delete, data change - NodeDataChanged
zk.exists("/a", true);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void processResult(int rc, String path, Object ctx, List children) {
KeeperException.Code code = KeeperException.Code.get(rc);
// 성공
if(code == KeeperException.Code.OK) {
if(children != null) {
// 조회된 child node 모두 watcher (getChildren, exists) 등록
for (String c : children) {
try {
zk.getChildren(path + "/" + c, true, this, null);
zk.exists(path + "/" + c, true);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) throws IOException {
Executor exec = new Executor();
exec.run();
}
@Override
public void run() {
try {
synchronized (this) {
while (true) {
wait();
}
}
} catch (InterruptedException e) {
}
}
}
[Watcher and Callback]
public class NodeMonitor implements Watcher, AsyncCallback.ChildrenCallback, AsyncCallback.StatCallback {
private Logger logger = LoggerFactory.getLogger(NodeMonitor.class);
private ZooKeeper zk;
private NodeManager manager;
public NodeMonitor(ZooKeeper zk, NodeManager manager) {
logger.debug("-------- Constructor --------");
this.zk = zk;
this.manager = manager;
}
/**
* watcher 는 node 감지에 소비되므로 감지시 재등록 해주어야 한다.
* 특정 node 하위의 모든 event 를 감시하려면 exists 와 getChildren 모두에 watcher 를 등록해야 한다.
* Watcher @Override node 변화 감지, operation 의 watcher 인자에 의해 실행
* @param event
*/
@Override
public void process(WatchedEvent event) {
String path = event.getPath();
logger.debug("event type {}, path {}", event.getType(), path);
String eventType = event.getType().name();
String eventState = event.getState().name();
System.out.println("## process: path : "+path+", eventType : "+ eventType + ", eventState: "+ eventState);
switch (event.getType()) {
case NodeChildrenChanged:
// 하위 노드 조회 메소드 - 하위 노드 조회가 성공하면 this.callback method - processResult - 후 처리
// getChildren watcher 재 등록
zk.getChildren(path, true, this, null);
break;
case NodeDataChanged:
logger.debug("node data path = {} ", path);
// watcher 재등록만을 위한 목적이면 callback = null
// NodeDataChanged 에 대한 후 처리가 필요한 경우 callback class 는 AsyncCallback.StatCallback 를 구현
// zk.exists(path, true, this, null);
try {
zk.exists(path, true);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
// exists callback method 에서 처리 하지 않고 process method 에서 처리 할 수 있음
// 물론 아래 로직을 exists callback 에 기술할 수 있음
try {
byte[] b = zk.getData(path, this, null);
String data = new String(b);
boolean isTestNode = manager.isTestNode(path, data, this::selectNodeData);
// manager.getNode(data);
logger.debug("node data changed {} - is test node {} : ", data, isTestNode);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
break;
}
}
private Node selectNodeData(String path, String data) {
Node node = manager.getNode(data);
node.setPath(path);
return node;
}
/**
* getChildren Callback - operation 실행 후 수행되는 callback method, operation 의 callback 인자에 의해 실행
* 즉 watcher 에 의해 변화를 감지하고 변화에 맞는 operation 을 수행했을때
* operation 의 지정된 callback 에 의해 processResult 가 실행
* @param rc
* @param path
* @param ctx
* @param children
*/
@Override
public void processResult(int rc, String path, Object ctx, List children) {
KeeperException.Code code = KeeperException.Code.get(rc);
logger.debug("CALLBACK - getChildren, path : {}, code : {} ", path, code);
// 성공
if(code == KeeperException.Code.OK) {
if(children != null) {
// do something...
// NodeChildrenChanged event 발생시 등록한 callback 에 의해 수행 될 비즈니스 로직
logger.debug("children {} ", children);
for (String c : children) {
logger.debug("child node {}" + c);
try {
// 하위의 하위가 변경된게 아니기때문에 하위의 getChildren call back 필요없음
zk.getChildren(path + "/" + c, true);
zk.exists(path + "/" + c, true);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 실패
else {
}
}
/**
* exists callback
* implements AsyncCallback.StatCallback
* @param rc
* @param path
* @param ctx
* @param stat
*/
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
}
}
※ operation 실행시 watcher parameter 의 true 와 watcher object 의 차이
true - default watcher 등록
watcher object - 인자로 전달되는 object 를 watcher 로 등록 (this 는 현재 object 를 watcher 로 등록)
2017년 2월 4일 토요일
GC Monitoring
[GarbageCollectorMXBean api 사용]
[jmx api 사용]
[ result ]
PS Scavenge - young gc
PS MarkSweep - full gc
※ 정리 필요함
for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
// 마지막 gc 정보
System.out.println("gc count : " + gcBean.getCollectionCount());
com.sun.management.GarbageCollectorMXBean sunGcBean = (com.sun.management.GarbageCollectorMXBean) gcBean;
// 마지막 gc duration time
System.out.println(sunGcBean.getName() + "last gc time : " + sunGcBean.getLastGcInfo().getDuration());
// 마지막 gc duration time = getLastGcInfo().getDuration()
System.out.println(sunGcBean.getLastGcInfo().getEndTime() - sunGcBean.getLastGcInfo().getStartTime());
// 마지막 gc 실행 정보
System.out.println(sunGcBean.getLastGcInfo());
}
[jmx api 사용]
try {
MBeanServerConnection server = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("java.lang:type=GarbageCollector,name=PS MarkSweep");
MBeanInfo info = null;
info = this.server.getMBeanInfo(objectName);
List names = Arrays.stream(Optional.ofNullable(info).map(MBeanInfo::getAttributes).orElse(new MBeanAttributeInfo[]{null})).map(MBeanAttributeInfo::getName).collect(toList());
names.forEach(n -> System.out.println(n + " : " + server.getAttribute(name, n)));
} catch (MalformedObjectNameException e) {
e.printStackTrace();
}
PS Scavenge - young gc
PS MarkSweep - full gc
※ 정리 필요함
jmx MBean
application 에서 감시할 항목을 MBean 으로 jmx 에 노출 시켜 application 을 모니터링 할 수 있다.
jmx (2번 3번은 은 하나의 파일로 super - sub class 형식으로 작성가능)
1. mbean interface 생성
2. mbean 구현체 생성
3. mbean 구현체 jmx 에 등록 코드 작성
4. java 실행 with jmx option
5. jvisualvm 확인
* 표준 MBean Interface 생성 (※ 주의 interface suffix -MBean)
- MBean 인터페이스는 감시할 항목 정의
public interface MonitoringMBean {
long getExecuteTime();
long getPollingCount();
long getChannelPercentage();
}
* MBean Interface 구현
- 구현체에서 jmx 에 등록할 항목의 값을 return 한다.
- interface 의 구현체의 접미사 MBean 을 제외하고 파일명이 같아야 한다.
public class Monitoring implements MonitoringMBean {
public long getExecuteTime() {
return MonitoringCounter.getInstance().getExecuteTime();
}
public long getPollingCount() {
return MonitoringCounter.getInstance().getPollingCount();
}
public long getChannelPercentage() {
return MonitoringCounter.getInstance().getChannelPercentage();
}
}
* 모니터링 값 설정
- 여러 객체에서 MonitoringMBean 구현체인 Monitoring 객체에 값을 지정 하려면 싱글톤 방식으로 값을 전달해야 한다.
public class MonitoringCounter {
private static MonitoringCounter counter;
private long executeTime;
private long pollingCount;
private long channelPercentage;
private MonitoringCounter() {
}
public static MonitoringCounter getInstance() {
if(counter == null) {
counter = new MonitoringCounter();
}
return counter;
}
public long getExecuteTime() {
return executeTime;
}
public void setExecuteTime(long executeTime) {
this.executeTime = executeTime;
}
public long getPollingCount() {
return pollingCount;
}
public void setPollingCount(long pollingCount) {
this.pollingCount = pollingCount;
}
public long getChannelPercentage() {
return channelPercentage;
}
public void setChannelPercentage(long channelPercentage) {
this.channelPercentage = channelPercentage;
}
}
* MBean 등록
public class MonitoringExporter {
private Monitoring monitoring;
public MonitoringExporter() {
try {
this.monitoring = new Monitoring();
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
// new ObjectName("임의의 MBean 명 :type=임의의 Type명");
ObjectName jmxObjectName = new ObjectName("SomeApp:type=SomeType");
server.registerMBean(monitoring, jmxObjectName);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
-Dcom.sun.management.jmxremote.port=9999(임의의 포트 - 사용자 설정)
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
MonitoringExporter exporter = new MonitoringExporter();
}
}
* application 실행 java option
-Dcom.sun.management.jmxremote.port=9999 (임의의 포트)
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
* application
public class SomeApp {
private Random rand;
public SomeApp() {
this.rand = new Random();
}
public void run() {
System.out.println("do something.....");
MonitoringCounter.getInstance().setChannelPercentage(rand.nextLong());
MonitoringCounter.getInstance().setExecuteTime(rand.nextLong());
MonitoringCounter.getInstance().setPollingCount(rand.nextLong());
}
public static void main(String[] args) throws Exception {
MonitoringExporter.main(null);
SomeApp someApp = new SomeApp();
while(true) {
someApp.run();
Thread.sleep(1000);
}
}
}
* jvisualvm 확인
- MBeans 항목은 별도 jvisualvm 플러그인 설치
visualvm > tools > plugins > Avaliable Plugins > VisualVM-MBeans
또는 http://visualvm.java.net/pluginscenters.html 에서 현재 visualvm 버전의 plugin search
public class JMXReader {
private MBeanServerConnection server;
/**
* local connection
*/
public JMXReader() {
this.server = ManagementFactory.getPlatformMBeanServer();
}
/**
* remote connection
* remote 정의
* 같은 서버(localhost) 이라도 java 로 실행시키는 app 이 다를 경우 remote 로 이해해야함
* @param url
* @throws IOException
*/
public JMXReader(String url) throws IOException {
JMXServiceURL serviceURL = new JMXServiceURL(url);
/* java.io.IOException: The client has been closed. 발생 - 발생이유는 좀더 분석이 필요함
try (JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL)) {
this.server = jmxConnector.getMBeanServerConnection();
}
*/
JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL);
this.server = jmxConnector.getMBeanServerConnection();
}
public String[] getDomains() {
try {
return this.server.getDomains();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
*
* @param objectName 정규식 및 null 가능
* null = all
* *:* = all
* SomeApp:*
* Some*:type:S*
*
* @return
*/
public Set getObjectNames(ObjectName objectName) {
try {
return Sets.newHashSet(
Optional.ofNullable(this.server.queryNames(
objectName
,null
)).orElse(Sets.newHashSet()));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
*
* @param objectName MBean
* @return
*/
public List getAttributes(ObjectName objectName) {
MBeanInfo info = null;
try {
info = this.server.getMBeanInfo(objectName);
} catch (Exception e) {
e.printStackTrace();
}
return Arrays.stream(Optional.ofNullable(info).map(MBeanInfo::getAttributes).orElse(new MBeanAttributeInfo[]{null})).map(MBeanAttributeInfo::getName).collect(toList());
}
/**
*
* @param objectName MBean
* @param attrName MBean 항목
* @return
*/
public Object getAttributeValue(ObjectName objectName, String attrName) {
try {
return this.server.getAttribute(objectName, attrName);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws MalformedObjectNameException, IOException {
// local
JMXReader local = new JMXReader();
System.out.println(local.getAttributeValue(new ObjectName("java.lang:type=GarbageCollector,name=PS MarkSweep"), "Name"));
System.out.println("-------------------------- local end");
// remote
JMXReader remote = new JMXReader("service:jmx:rmi:///jndi/rmi://127.0.0.1:9999/jmxrmi");
Arrays.stream(remote.getDomains()).forEach(System.out::println);
System.out.println("-------------------------- domains end");
remote.getObjectNames(new ObjectName("*:*")).forEach(System.out::println);
System.out.println("-------------------------- objectNames end");
remote.getAttributes(new ObjectName("SomeApp:type=SomeType")).forEach(System.out::println);
System.out.println("-------------------------- attributes of SomeApp MBean end");
System.out.println(remote.getAttributeValue(new ObjectName("SomeApp:type=SomeType"), "ExecuteTime"));
System.out.println("-------------------------- attribute value of SomeApp MBean ExecuteTime Attribute end");
}
}
피드 구독하기:
덧글 (Atom)
Intelij 설정 및 plugin
1. preferences... (settings...) Appearance & Behavior > Appearance - Window Options ✓ Show memory indicator Editor ...
-
intellij 또는 eclipse 로 개발하다가 소스화면을 분할해서 봐야 할 경우가 생길때 해당 소스의 상단 탭에서 마우스 우클릭하면 탭 분할과 관련된 기능을 선택할 수 있다. 또는 아래와...
-
일반 배열 키보드나 맥북이 아닌 대다수의 노트북 키에는 있는 Context Menu Key(보통 우측 alt 키 또는 우측 윈도우키 오른쪽에 위치) 가 있고 이 키는 마우스 우클릭 했을때의 기능을 한다. 이미지 출처 : http://scls.t...
-
외부망 접속시 proxy 를 꼭 거쳐야 하는 환경에서 개발을 할때 browser 에서 접속이 되는 사이트는 다른 프로그램 (예: ide, maven) 에서도 접속이 가능하다. browser 가 단지 네트워크 연결을 proxy 서버 를 사용하는 것 ...
