티스토리 뷰

fwrm 서비스를 개발 및 코드 리뷰(1)


[짧고 굵게 보기] - 귀찮으면 여기 부분만 읽어도...


알파 버전 만들기

- 사실 상 이 서비스를 만들기 위해 어떤 스택이 필요한지 연구할 시간이 부족하였다. 주어진 시간은 고작 3일.

서비스 시연을 하기 위해(?) 3일간 밤새서 만들긴 하였다.

- 우분투 1개 서버만 돌리자.

- ftp 서버에서 데이터를 받아오는 작업과 디바이스에서 측정되는 1분단위 데이터를 모아서 차트 및 표로 보여줘야하는 작업이었다.

- 웹 및 앱 모두 만들어야 한다. 시간이 3일 밖에 없으니 웹뷰로 처리하기.

- 사용자들은 지난 데이터들을 하루/3일/7일 등등 범위 값을 기준으로 볼 수 있어야한다.

- 데이터를 기반으로 특정 분석 알고리즘으로 결과를 표출해서 푸쉬 알림을 줘야한다.

- 앞서 푸쉬 알람을 준 데이터들을 pdf에 작성되어야한다.

- 사용자의 디바이스에 받은 데이터를 엑셀 파일 및 pdf 파일로 다운 받을 수 있어야한다.




베타 버전 만들기(알파에서 수정 및 추가)

- 일단 시간이 조금 넉넉해졌다. 4~5개월 정도?!

- 웹과 앱을 역할을 분리시키기. 웹의 역할과 앱의 역할을 분리시켜 API 만들기

- 1분 단위 데이터 변화가 엄청나게 크지 않으므로 5분 단위로 조정하였다. (다행이다.)

- ftp 서버에서 받아오는 데이터들 또한 1분 단위가 아닌, 1시간 단위로 받아오게 하였다.

- 앱에서 차트와 표로 보여주는 데이터가 너무 많으면, 사용성에서 불편하므로 간단하게 텍스트로 처리하였다.

- 알고리즘 연산을 cron-job으로 class화 시켜 특정 월에만 사용하는 method를 불러오는 형식으로 처리하였다.




물론 베타 버전이라는 네이밍이 붙는 이유는  완벽하지 않기 때문이다. 여전히 TDD를 하여야하고 성능향상을 위해서 알고리즘을 계속 수정하고 업데이트 해줘야한다. 




(1) FTP 서버에서 데이터 받아오기

[알파버전]

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
# Get 1min AWS raw data
@kronos.register('*/1 * * * *')
def Getdata_Products():
    ftp = FTP('FTP-host-ip',"id","password")
    # 해당 폴더까지 이동
    ftp.cwd('/')
    ftp.cwd('work1')
    ftp.cwd('KMA')
    ftp.cwd('3AWS')
    # 해당 폴더의 파일 append
    file_list = []
    ftp.retrlines("LIST", file_list.append)
    # 8조각을 내서 파일명 추출
    file_list[-1].split(None,8)
    filename = file_list[-1].split(None,8)[-1].lstrip()
    if len(AWS_Min_rawdata.objects.filter(filename=filename)) != 0:
        pass
    else:
        # local에 복사하기
        local_filename = os.path.join(r"/home/ubuntu/ftp_data/", filename)
        lf = open(local_filename, "wb")
        ftp.retrbinary("RETR " + filename , lf.write, 8*1024)
        lf.close()
        base_path = '/home/ubuntu/ftp_data/'
        file_path = base_path + filename
        paths, files = os.path.split(file_path)
        os.renames(file_path, base_path+files+'.txt')
        # Read and textfiled add
        read_aws = open(base_path+filename+".txt").read().split("=\n")
        # 예외처리 / 만약 있으면, 거기서 끝 아니면 생성
        rawdata = ""
        for aws_get in aws_number.objects.all():
            for each in read_aws:
                if each[0:3== unicode(aws_get.get_aws_number):
                    rawdata += "AWS_"+each+"\n"
 
                    
 
        update_data = AWS_Min_rawdata.objects.get_or_create(filename=filename,rawdata=rawdata)
cs

django-kronos라는 아주 훌륭한? 라이브러리를 사용했다.


FTP서버로 부터 새로운 데이터를 1분 단위로 cron으로 받아오게 했다. 사실 쉘 스크립트를 짜는 능력이 return 0 이기에 python을 이용해서 cron을 돌리기로 하였다. 사실 여기서 처음 해보는 작업들이 많아서 많은 오류와 버그... 성능 저하를 보여줬다.


게다가 1분 단위로 새롭게 갱신되는 파일 확장자가 없는(?) raw 데이터 파일을 받아서 읽어야 했다. 물론 이 파일이 1분에 1개 많게는 3~5개씩 생성되어버리면서 잘 작동이 안되었다. ㅠㅠ 그냥 서비스 시연용이라 django-admin 테이블에 보여주기만 해도 되어서 너무 대충짰던탓일까... 알파버전을 갈아엎고 새롭게 만들기 시작했다.


일단 알파버전에서는 ftp 서버의 해당 폴더까지 이동하고서 폴더의 파일 리스트를 모두 찍어내고, 파일명을 추출하는 작업을 거치고 우분투 서버에 파일을 새롭게 만들고 거기에 복사하고서 확장자가 없으니 txt로 이름을 변경해주었다. 텍스트 파일로 변경된 데이터들을 "=\n"으로 split 시키고.... 글로 쓰려고 해도 어마어마하게 복잡한 순서가 나온다...


1분 단위로 처리하려하니 막상 우분투 서버가 말을 듣지 않는다....




베타 버전에서는 다음과 같이 변경되었다.


[베타버전]

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@kronos.register('10 */1 * * *')
def Getdata_FTP():
    filename = "AWS_1HR_" + (datetime.now() - timedelta(minutes=10)).strftime("%Y%m%d%H")
    """
    check S3 file exsits
    """
 
    if platform.system() == "Linux":
        paths = "/ubuntu-path"
 
    else:
        paths = "/local-path/"
 
 
    if os.path.exists(paths + filename + ".txt"):
        raise Exception("Error - Not Exists file - %s" %(filename))
 
    else:
        try:
            # ftp login
            ftp = FTP('FTP-host-ip'"id""password")
            ftp.cwd('/work1/KMA/3AWS')
 
            locals = os.path.join(paths, filename + ".txt")
            try:
                load_data = open(locals)
            except:
                load_data = open(locals, "wb")
            ftp.retrbinary('RETR %s' % filename, load_data.write)
            load_data.close()
 
        except:
            raise Exception("FTP - Server Some Error")
 
        folder_dir = datetime.now().strftime("%Y%m%d"+ "/"
 
        s3 = boto3.resource('s3''ap-northeast-2')
 
        # upload data
        with open(paths + filename + ".txt"'rb') as data:
 
            s3.Bucket('bucket-name').put_object(Key="AWS" + folder_dir + filename + ".txt", Body=data)
 
 
        # delete local files
        os.remove(locals)
 
        # RDS create
        isURL = "url-s3-path" + datetime.now().strftime("%Y%m%d") \
                    + "/AWS_1HR_" + datetime.now().strftime("%Y%m%d%H"+ ".txt"
        IsS3data = urllib.urlopen(isURL)
 
        readData = IsS3data.read().split("=\n")
 
        for lines in readData:
 
            for each in Device.objects.all():
 
                if lines[0:3== str(each.aws_number.aws_number):
                    AWS_FTP_Data.objects.get_or_create(aws=each.aws_number, origin_data=filename, windDirection= round(lines.split("#")[5]*0.1,1), windSpeed=round(lines.split("#")[6]*0.1,1), temp=round(lines.split("#")[7]*0.1,1), humi=round(lines.split("#")[8],1) )
 
 
cs


대충 도입부만 봐도 조금(?) 깔끔해진 느낌...만 든다. 우선 로컬에서 테스트할 때 마다 path를 신경써줘야했는데 그 귀차니즘 때문에 그냥 platform.system()으로 우분투인지 맥인지 체크하여 path값을 만들어줬다. 그리고 해당 path에 해당 파일이 존재하면 안되므로(?) 걍 exception 처리해버렸다. (물론 실제 업로드 할 때는 그러지 않음....)


그리고 앞서 변수로 만든 path에 해당하는 우분투 로컬 파일을 확장자를 붙여서 생성했다. 그리고 뒤에도 나오겠지만 우분투 서버에 쌓기보다 그냥 S3에 올려버리자! 생각했다. (한마디로 우분투 로컬에는 더미로 만든다!) 역시나 해당 파일을 읽어서 복사하자. 


폴더 이름을 1시간 단위 데이터로 변경되었기 때문에 조금 더 간편해졌다. 사실 그리고 앞서 특정 파일들은 1시간 단위로 만들어지는게 아니라, 1시간 1분에서 1시간 10분 사이로 만들어지는 것을 확인했다. (물론 여기서 짜증나는 부분은 특정 파일들이 1시간에 1개씩이 아니라, 1~3개씩 만들어져서 ommit 되어버리는 귀찮은 상황이 발생하였다.... 이건 고쳐야지....)

S3에 업로드할 때 boto3를 이용해서 put_object로 처리했다. 그리고 우분투 로컬에 데이터 쌓이길 원하지 않기 때문에 삭제했다.


그리고 매번 S3의 데이터를 조회해서 주소값을 받아오기가 귀찮아(빨리 테스트해보려고)서 다이렉트로 앞 url을 만들어주고 해당 날짜별로 폴더를 만들어 접근하게 했다. 그리고 해당 파일을 urllib로 읽어들여서 마찬가지로 create하였다.


물론 여기서 로컬에서 데이터를 읽어서 create하는 방법이 있긴하지만.... 성능 면에서 어느 것이 더 좋은지는 조금 더 테스트해보고(당연히 로컬에서 읽는게 빠르겠지?) 코드를 수정해야겠다. 


그리고 해당 데이터 전부를 저장하는것이 아니라 해당 디바이스 아이디 별로 데이터를 create하여 앱이나 웹에서 데이터를 조회할 때 별도 처리를 하지 않아도 되게 했다.


그렇다면 굳이 왜? S3에 저장해야하는가? 이 부분은 나중에 디바이스가 추가될 때 해당 디바이스에서는 등록된 이후 부터 데이터를 create하기 때문에 과거의 데이터를 볼 수 없다. 따라서 새롭게 추가되는 디바이스에 대한 과거 데이터를 등록하기 위해서 원본 파일이 S3에 저장되고 있어야한다.








댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함