참고 링크
 
  1. 배경 및 목적
    1. 요즘 MSA 등의 형태로 서비스를 만들다보니 외부 API호출하는 경우가 많음(또는 AWS 서비스 호출)
    2. 네트웤 문제나 스로틀링 등의 문제로 가끔 실패가 발생하는 경우가 있음. 실패가 발생할 경우 재 처리가 필요하여 사용
 
 
 
예제 소스 (from AWS)
  •  지수 백오프의 기본 아이디어는 오류 응답이 연이어 나올 때마다 재시도 간 대기 시간을 점진적으로 늘린다는 것.
  • 최대 지연 간격과 최대 재시도 횟수를 구현해야 함. 최대 지연 시간 간격과 최대 재시도 횟수는 고정 값일 필요가 없으며, 수행 중인 작업과 다른 로컬 요인(예: 네트워크 지연 시간)을 기반으로 설정
java 버전의 재 시도 대기시간 구하는 메소드 샘플
* 참고로 jitter를 적용하는게 좋음(위 aws글 참고
Thread.sleep(RetryUtil.getRetrySleepTime(retryCnt, 2, 1000, 2000)); //대기처리

	
/**
* 재 시도시 대기시간(sleep) 구하기
*  - 지수 백오프 알고리즘 기반
*
* @param retryCount 요청하는 재시도 횟수
* @param increaseBase 증가 처리의 지수의 밑(ex. 2^4 에서 2)
* @param increaseMillis 매 재 요청마다 증가하는 시간(밀리시간초)
* @param maxWaitMillis 최대 대기시간(밀리시간초). 매 증가하는 대기 시간은 최대 대기시간을 넘을 수 없음
* @return
*/
public static long getRetrySleepTime(int retryCount, double increaseBase, long increaseMillis, long maxWaitMillis) {

    long waitTime = ((long)Math.pow(increaseBase, retryCount) * increaseMillis); //ex) ((long)Math.pow(2, retryCount) * 100L);  => 100, 200, 400, 800, 1600과 같이 증가함
    return Math.min(waitTime, maxWaitMillis); //두번째 argment은 최대 대기시간(밀리세컨드)
}



 
AWS 샘플 소스
public enum Results {
    SUCCESS,
    NOT_READY,
    THROTTLED,
    SERVER_ERROR
}
 
 
/*
* Performs an asynchronous operation, then polls for the result of the
* operation using an incremental delay.
*/
public static void doOperationAndWaitForResult() {
 
 
    try {
        // Do some asynchronous operation.
        long token = asyncOperation();
 
 
        int retries = 0;
        boolean retry = false;
 
 
        do {
            long waitTime = Math.min(getWaitTimeExp(retries), MAX_WAIT_INTERVAL);
 
 
            System.out.print(waitTime + "\n");
 
 
            // Wait for the result.
            Thread.sleep(waitTime);
 
 
            // Get the result of the asynchronous operation.
            Results result = getAsyncOperationResult(token);
 
 
            if (Results.SUCCESS == result) {
                retry = false;
            } else if (Results.NOT_READY == result) {
                retry = true;
            } else if (Results.THROTTLED == result) {
                retry = true;
            } else if (Results.SERVER_ERROR == result) {
                retry = true;
            }
            else {
                // Some other error occurred, so stop calling the API.
                retry = false;
            }
 
 
        } while (retry && (retries++ < MAX_RETRIES));
    }
 
 
    catch (Exception ex) {
    }
}
 
 
/*
* Returns the next wait interval, in milliseconds, using an exponential
* backoff algorithm.
*/
public static long getWaitTimeExp(int retryCount) {
 
 
    long waitTime = ((long) Math.pow(2, retryCount) * 100L);
 
 
    return waitTime;
}
 

 


메모

 - 2019-10-11 글


https://quickblox.com/blog/why-quickblox-choose-xmpp-protocol/

https://infrastructure.aws/


멋있게 나옴



주의: 개인적인 메모 목적으로 정리한 글입니다.


ejabberd XMPP서버와 Mysql 조합

  1. 필요조건
    1. ejabberd는 FULLTEXT index를 InnoDB엔진과 함께 사용 -> Mysql 5.6 이상이 설치되어야 함
  2. Amazone AWS의 Mysql RDS 지원함


Mysql ejabberd 셋팅
  1. DB 접근 권한 추가
echo "GRANT ALL ON ejabberd.* TO 'ejabberd'@'localhost' IDENTIFIED BY 'password';" | mysql -h localhost -u root -p

  1. . DB 생성
echo "CREATE DATABASE ejabberd;" | mysql -h localhost -u ejabberd -p

  1. DB 접속 확인
echo "CREATE DATABASE ejabberd;" | mysql -h localhost -u ejabberd -p

  1. Mysql 스키마 적용
cd /opt/ejabberd-19.09.1/lib/ejabberd-19.09.1/priv/sql (예. 설치된 디렉토리 하위의 sql 디렉토리에 스키마 sql들이 존재함)
mysql -h localhost -D ejabberd -u ejabberd -p < mysql.sql

참고: new sql 파일이 존재하는데 여러개의 XMPP도메인 사용할 때인 듯(참고 링크

[참고-생성 결과 테이블 캡쳐]


  1. ejabberd 설정 진행

참고: https://docs.ejabberd.im/admin/databases/mysql/

vi /opt/ejabberd/conf/ejabberd.yml 후 아래 내용 추가

## MySQL server:
##
sql_type: mysql
sql_server: "localhost"
sql_database: "ejabberd"
sql_username: "ejabberd"
sql_password: "암호입력"
##
## If you want to specify the port:
sql_port: 3306


#유저 암호를 기본설정인 internal에서 db를 사용한다는 설정 추가
auth_method: sql
auth_password_format: scram

# modules의 기본 저장을 DB로 변경(각 module별로 개별 설정도 가능)
default_db: sql

#유저 세션을 DB에 저장(redis 설정 후 sm_db_type: redis 로 설정하면 redis를 사용). 참고: https://docs.ejabberd.im/admin/configuration/#database-and-ldap-configuration
#Mysql의 경우 sm 테이블에 저장됨
sm_db_type: sql



  1. 참고: 추가 설정이 없으면 유저 암호가 plain text로 저장됨

    1. 아래 설정으로 변경해서 암호화해서 저장되도록 변경
vi /opt/ejabberd/conf/ejabberd.yml 후 아래 내용 추가

auth_password_format: scram

이후 계정 생성해보면 암호화되어서 저장됨
ex) ./ejabberdctl register "test-enc" "localhost" "test-enc" 로 계정 생성 후 DB 조회해봄
      1. 암호화되서 저장된 결과 캡쳐



XMPP프로토콜의 ejabberd 채팅 서버 셋팅 2 - client 설치 후 실제 채팅 진행해보기


  1. windows 클라이언트 spark를 설치 후 채팅을 진행해봄
    1. ejabberd와 함께 많이 쓰는 java기반의 openfire의 client를 사용
    2. 설정 샘플(로컬 virtualbox에 ejabberd설치되어 있음)


    3. virtual box 포트 포워딩 후 PC 1대 노트북 1대로 채팅을 진행해봄


    4. 로컬 개발PC 윈도우의 방화벽 5222 포트를 허용해야 될 수 있음

  1. ejabberd 서버설치
RPM 패키지로 설치
참고: https://www.process-one.net/en/ejabberd/downloads/ 사이트에서 RPM 패키지 URL확인 가능

예)
wget 'https://www.process-one.net/downloads/downloads-action.php?file=/ejabberd/19.09.1/ejabberd-19.09.1-0.x86_64.rpm' -O ejabberd.rpm
rpm -ivh ejabberd.rpm


#서비스로 시작하기 위해서 복사
cp /opt/ejabberd-18.12.1/bin/ejabberd.service /etc/systemd/system/

#systemctl enable(서버 재 시작시 ejabberd도 시작됨)
systemctl enable ejabberd

#서비스 시작
systemctl start ejabberd

#확인
systemctl status ejabberd

  1. 설치된 서버 확인
    1. 리슨포트 확인
      1. netstat -anp | grep 'LISTEN' 명령어로 확인
        1. 5222포트는 메신저용, 5280은 admin web용


  2. 자주 쓰는 명령어
cd /opt/ejabberd-19.09.1/bin 후(설치한 버전에 따라서 다름)


-- 등록된 계정 리스트 조회
./ejabberdctl registered_users 호스트(ex. localhost)


-- 신규 계정 등록
./ejabberdctl register ID입력 서버 호스트(ex. localhost) 암호


-- 설정변경(ex. admin 계정)
vi /opt/ejabberd/conf/ejabberd.yml



아는 사람은 모두 알겠지만 Elasticsearch쪽이 클라우드가 기여 안하고 사용만한다고(돈벌어간다고 ㅎㅎ)해서 라이센스 일부를 변경함(ex. AWS가 fork 해서 'Open Distro for Elasticsearch' 만듬)



Elasticsearch 6.3버전부터는 x-pack code가 Elastic License하에 배포되고 있으니 회사에서 사용하려면 라이센스 계약을 해야함


x-pack feature를 사용하지 않는다면, 해당 코드를 제외하고 설치해야함



혹, 잘못된게 있으면 댓글 달아주세요.

'ElasticSearch > ElasticSearch' 카테고리의 다른 글

Elasticsearch Performance Tuning  (0) 2021.03.08
import java.util.Properties;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.search.FlagTerm;
/**
 * IMAP 이메일을 가져오는 샘플 소스
 *  - 주의: 간단히 POC한 소스라서 예외처리 및 부가처리는 완벽히 안되어 있음
 *
 * @author 엄승하
 */
public class IMAPEmailGetSample {
     public static void main(String[] args) throws Exception {
          System.out.println("-- IMAP Emal 가져오기: Start\n\n");
          String host = "입력필요"; //imap 호스트 주소. ex) imap.gmail.com
          String userEmail = "입력필요"; //유저 이메일 주소
          String password = "입력필요"; //유저 암호
          IMAPMailService mailService = new IMAPMailService();
          mailService.login(host, userEmail, password);
          int messageCount = mailService.getMessageCount();
          //테스트 목적이라서 5개 초과이면 5개만 처리: TODO 삭제
          if (messageCount > 5) {
               messageCount = 5;
          }
          Message[] msgArray = mailService.getMessages(false);
          for (int i = 0; i < messageCount; i++) {
               Message msg = msgArray[i];
               if (msg.getSubject() != null) {
                    System.out.println(String.format("컨텐츠타임: %s", msg.getContentType()));
                    System.out.println(String.format("발신자[0]: %s", msg.getFrom()[0]));
                    System.out.println(String.format("메일제목: %s", msg.getSubject()));
                    String mailText = mailService.getEmalText(msg.getContent());
                    System.out.println(String.format("메일내용: %s", mailText));
               }
          }
          mailService.logout(); //로그아웃
          System.out.println("\n\n-- IMAP Emal 가져오기: 종료");
     }
}
/**
 * IMAP 관리 inner클래스
 *  - 참고: https://javapapers.com/java/receive-email-in-java-using-javamail-gmail-imap-example/
 * 
 * @author 엄승하
 */
class IMAPMailService {
     private Session session;
     private Store store;
     private Folder folder;
     // hardcoding protocol and the folder
     // it can be parameterized and enhanced as required
     private String protocol = "imaps";
     private String file = "INBOX";
     public IMAPMailService() {
     }
     public boolean isLoggedIn() {
          return store.isConnected();
     }
     /**
      * 메일 본문 텍스트 내용을 가져옴
      *
      * @param content
      * @return
      * @throws Exception
      */
     public String getEmalText(Object content) throws Exception {
          //TODO: 개발 필요
          System.out.println("####  컨텐츠 타입에 따라서 text body 또는 멀티파트 처리 기능 구현이 필요");
          if (content instanceof Multipart) {
               System.out.println("Multipart 이메일임");
          } else {
               System.out.println(content);
          }
          return null;
     }
     /**
      * to login to the mail host server
      */
     public void login(String host, String username, String password) throws Exception {
          URLName url = new URLName(protocol, host, 993, file, username, password);
          if (session == null) {
               Properties props = null;
               try {
                    props = System.getProperties();
               } catch (SecurityException sex) {
                    props = new Properties();
               }
               session = Session.getInstance(props, null);
          }
          store = session.getStore(url);
          store.connect();
          folder = store.getFolder("inbox"); //inbox는 받은 메일함을 의미
          //folder.open(Folder.READ_WRITE);
          folder.open(Folder.READ_ONLY); //읽기 전용
     }
     /**
      * to logout from the mail host server
      */
     public void logout() throws MessagingException {
          folder.close(false);
          store.close();
          store = null;
          session = null;
     }
     public int getMessageCount() {
          //TODO: 안 읽은 메일의 건수만 조회하는 기능 추가
          int messageCount = 0;
          try {
               messageCount = folder.getMessageCount();
          } catch (MessagingException me) {
               me.printStackTrace();
          }
          return messageCount;
     }
     /**
      * 이메일 리스트를 가져옴
      *
      * @param onlyNotRead 안읽은 메일 리스트만 가져올지 여부
      * @return
      * @throws MessagingException
      */
     public Message[] getMessages(boolean onlyNotRead) throws MessagingException {
          if (onlyNotRead) {
               return folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
          } else {
               return folder.getMessages();
          }
     }
}

+ Recent posts