Evernote logo


에버노트는 스마트폰, PC를 상관하지 않고 어디서든 노트를 쓰고, 읽고, 쓴 글을 검색할 수 있는 노트 어플리케이션 서비스를 제공하고 있습니다. 또한 로컬에도 저장을 할 수도 있습니다.



에버노트는 컴퓨터 공학의 선구자였던 "베네바 부시"의 memex를 생각하면 만든 것은 아니었을 까요?



사용자 삽입 이d미지



 베네바 부시는 인간이 가지고 있는 정보를 간편히 저장하고 빠르게 검색할 수 있는 소형의 시스템이 필요하다고 했습니다. 그래서 많은 사람들은 웹은 베네바 부시가 생각했던 부산물이었다고 부산물이라고 생각했습니다.

인간이라는 것이 나라고 생각한다면, 내가 가지고 있는 (노트)정보를 간편히 저장하고 빠르게 검새할 수 있는 소형의 시스템은 에버노트가 될 수도 있겠죠?


ㅎ이 생각은 제가 아니라 이미 있는 얘기입니다.  ㅎ

(http://blog.jonudell.net/2008/04/07/a-conversation-with-phil-libin-about-evernotes-new-memex/)



이제 본론으로 들어가도록 하겠습니다.


이 에버노트에 대한 아키텍처를 지난 5월에 자신의 기업 블로그(http://blog.evernote.com/tech/2011/05/17/architectural-digest/#)를 공유하였습니다.






큰 그림으로 보이는 이 한장의 그림이 아키텍쳐입니다.

서버와 통신하기위해서는 방화벽 - LB(로드 밸런스) 스위치 - 웹 서버 를 거쳐야 합니다.


방화벽은 Vyatta 제품을 쓰고 있습니다. 트래픽과 보안을 위해서 올해 1월에 추가했습니다. 이 제품은 외부와 통신하기 위해서 BGP(외부 게이트웨이와 통신할 수 있는 프로토콜)/Level3/NTT 을 지원하고 있습니다. AX2500 이라는 장비를 써서 failover에 대응하고 있습니다. 추후, N+1 redudancy(시스템 가용성을 위한 내용, 여러 장비가 동시에 사용할 수 있도록 하는 것을 의미, master/slave와 대비하는 용어)를 적용할 예정입니다.


A10 스위치 장비의 SSL 가속화 기능을 이용하여 비싼 SSL 소요 비용을 최소화하여 성능을 높였습니다. 즉 SSL 통신 때문에 앞단 어플리케이션 서버가 부하를 최대한 줄였습니다. 


웹 서버는 Thrift 기반의 Service API를 이용하여 하루에 최고 약 250 mbps로 들어오는 트래픽의 150만 Https 요청을 처리하고 있습니다. 계산해보니 초당 17 tps 정도를 카버할 수 있는 용량입니다. 정확하지는 않지만, A10  스위치 장비에 thrift를 올린 것 같기도 하구요. SSL 가속기가 달린 스위치로부터 필터받은 요청을 thrift에서 처리한 것으로 생각하고 있습니다.


VM 을 이용해서 서버를 운영하고 있습니다. 

server farm을 shads라 불리우는데, 한 개의 shard는 십만명의 user를 처리할 수 있습니다. 다.따라서, 9백만 사용자의 90개의 shards로 운영할 수 있습니다. 2개의 쿼드코어 칩을 가진 슈퍼마이크로 (http://www.supermicro.com/index.cfm)에 Segate RAID 장비 사용, 여기에 2개의 Xen VM을 설치하여 운영하고 있습니다.


서버 이중화를 설명드리겠습니다. 한대의 VM(primary)에 WAS를 구성하고, 다른 한대의 VM(secondary) 은 앞에 설치된 WAS를 DBRD 솔루션을 이용해서 replication하도록 하고, Heartbeat를 이용해서 failover처리가 되도록 하였습니다. 


DBRD가 생소한 내용일텐데, 아래 그림을 보시면 이해가 좀 편합니다. 일종의 Replication 솔루션이라고 보시면 됩니다.

  



NoteStore에 사용하는 WAS와 DB 구성방식은 다음과 같습니다. 

Debian + Java 6 + Tomcat + Hibernate + Ehcache +  Stripes + GWT + MySQL (for metadata) + hierarchical local file systems (for file data).


현재의 90개 이상의 shard가 있습니다.  하나의 shard 에 특정 사용자가 바인딩됩니다. 그리고, 사용자 정보가 하나의 shard에 저장되면, 다른 shard와는 통신하지는 않습니다. 따라서, load balancer의 thrift에서는 사용자 정보를 판별하는 로직이 들어가 있습니다. 


사용자 정보와 데이터를 mysql(UserStore라 불리움) 에 저장됩니다. mysql은 백업을 위해서 RAID 미러링과 DBRM 솔루션을 이용합니다. 

mysql은 in-memory 기반이며,  id, md5 passwod, shard id로 데이터필드를 구성하였습니다. 


에버노트는 AIR(Advanced Imaging and Recognition)라고 하는 이미지에서 검색하는 pool 서버가 있습니다. 이 서버는 자체 개발한 솔루션이며, 핸드라이팅같은 거 검색하는데 사용하고 있습니다. 

총 8코어짜리 28대의 서버가 있고, 윈도우와 데비안 OS를 사용중이며, 올해 말까지 윈도우 서버를 빼서 모두 데비안 OS로 바꿀 예정입니다. 


기타 서버를 말씀드리겠습니다. 

SMTP 서버는 Debian 위에  sendmail 어플인 postfix(http://www.postfix.org/)와 Dwarf 네트웍 AS 서버(http://www.gnome.sk/Dwarf/dwarf.html)를 기반으로 Java mailer 이용해서 사용하고 있습니다. 

트위터 게이트웨이는 twitter4j기반으로 in-house용으로 개발된 것을 쓰고 있습니다.

모니터링 솔루션은 Zabbix, Opsview, AlertSite을 이용하고 있으며,  환경설정파일 배포는 puppet(http://www.puppetlabs.com/puppet/introduction/)을 이용하고 있습니다.


Reference

http://highscalability.com/blog/2011/5/23/evernote-architecture-9-million-users-and-150-million-reques.html

http://blog.evernote.com/tech/2011/05/17/architectural-digest/#


Posted by 김용환 '김용환'

You may  meet java errors every day if you are a java developer. Especially if a lot of or critical error logs in web application get to meet, you will be panic. So, there is need to develop error log DB and error-collectiong log module.
I will introduce my idea about developing error log DB.
According to your company policies, methods are applied for error logging system.

Architecture of error logging system consists of
1) Error DB can be stored
2) Admin web application can show errors on error db
3) Java classes and log4j configuration to transfer logs to DB.



<Error DB>
In web application, there are many properties to save such as belows.
project-name, server-name, loginid(userid), access date-time, log level, url, form, cookie, referer, execpetionmessage 

You can specify those more concisely. For example, exceptionmessage is divided to head and body according to java.lang.Exception.

I made error log db, table. Specially I made indexing in mysql. And in mysql DB, I made a batch script to rotate log db.

#!/bin/sh
TODAY=`date -d "-1 day" +%Y%m%d`
TO_DELETE_DAY=`date -d "-30 day" +%Y%m%d`
mysql  -u userid -p password mysql db-schema-name << !


# rotate log table 
alter table lgt_logdata rename logdata_${TODAY}; 


drop table if  exists logdata_${TO_DELETE_DAY};

create table logdata (
  logid int(11) NOT NULL auto_increment,
  projectname varchar(30) ,
  servername varchar(20) ,
....
  KEY lgt_logdata_ix1 (projectname,loglevel)
)



<Error-collectiong>
Error-collecting log module is implemented by log4j in simple. You can change the properites to save several log-level like error, warning. So, you can make warning log DB as well.


Let us suppose to save the errors to mysql. 
<Log4j.xml>

<appender name="ERROR-OUT" class="org.apache.log4j.jdbcplus.JDBCAppender">
<param name="Threshold" value="ERROR"/>
<param name="dbclass" value="com.mysql.jdbc.Driver"/>
<param name="url" value="jdbc:mysql://in.log.google.com"/>
<param name="username" value="error"/> 
<param name="password" value="nobodynobodywantyou"/>
<param name="sqlhandler" value="com.google.kr.MySQLHandler"/>
<param name="Buffer" value="1"/>
</appender>


<logger name="com" additivity="false">
<level value="DEBUG"/>
            <appender-ref ref="ERROR-OUT"/>
</logger>


You have to save error-loging policy in your web application.
I think every logs have to store error log DB, so, I have policy to save all tomcat logs. Because every any kinds of errors can affect the performance of web application.

I have three policies to store the DB. I think those are very important to design error DB.
1. Using Interceptor in web work or struts2, web data such as url, form have to save MDC. (MDC is api of log4j framework)

Before web application meet java exceptions, the interceptor save the web information and after invoking action it checks the result of invoking action are failed(or throw exception). If the action throws the exception, those errors will sqlhandler (ERROR-OUT)

MDC.put("projectname", projectname);
MDC.put("cookie",cookie);

2. Use Servlet in web.xml. 

You may extend and implements extends javax.servlet.http.HttpServlet class. Through the implemented class to handle error, you can store errors.

<servlet>
<servlet-name>errorHandler</servlet-name>
<servlet-class>com.google.kr.ErrorHandlerServlet</servlet-class>
</servlet>
3. Adding some fuctions to sqlhandler.
Most of error cases occur in calling action from users, but in some case internal web application invoker can make errors. For example batch or cache or scheduling based on Thread could make fault, but belows method can not include saving error logs.
And then, when you implement sqlhandler, you can add those.
You've got to adds some exceptional case on the sqlhandler.

import org.apache.log4j.jdbcplus.JDBCSqlHandler;
public class ErrorHandler implements JDBCSqlHandler {
...
}


<web applicatiion for error log>
You can show the errors stored in DB in simple.

Posted by 김용환 '김용환'