前提・実現したいこと
python heroku flaskを使ってline botを作ろうとしています。しかしheroku openをしてみるとapplication errorが出てしまい、ログを見るとH12 timeoutなどのメッセージがあったのでそれについて調べてみると30秒以内に処理しないといけないらしい。処理が重すぎるとなるらしいです。無限ループとかも原因の一つと書かれている記事を見ました。その対処法がバックグラウンドで処理するといいらしいです。そこでいまはバックグラウンド処理をrqで実装しようとしているのですが、考え方がわからないところが出てきました。私は元々外国為替のチャートのデータを定期的に読み込んで特定のシグナルが出たらそれをLINEに通知するというモノを作ろうとしていたのですが、その読み込みと計算の処理が重いのかなと思いmain.pyとtask.pyの二つのファイルに処理を分けました。task.pyで計算してmain.pyの中にあるmessage1関数を実行したいのですがお互いのモジュールがインポートし合うことはできないのでどうすればいいかわかラナイ状態です。そもそもrqでバックグラウンド処理をすることで解決できるのか確信もありません。説明不足なところがあるかもしれませんが素敵な回答お待ちしています。よろしくお願いします。
あとLINE-APIのようなモノをローカルで実験する方法を知りたいです。そうすればもう少し自分で解決できるようになると思うので。
発生している問題・エラーメッセージ
エラーメッセージ
該当のソースコード
main.py
# import library from flask import Flask, request, abort from linebot import ( LineBotApi, WebhookHandler ) from linebot.exceptions import ( InvalidSignatureError, LineBotApiError ) from linebot.models import ( FollowEvent, MessageEvent, TextMessage, TextSendMessage, ImageMessage, ImageSendMessage, TemplateSendMessage, ButtonsTemplate, PostbackTemplateAction, MessageTemplateAction, URITemplateAction ) import os import time import schedule import datetime from task import perfect_order_USDJPY from rq import Queue from worker import conn # 軽量なウェブアプリケーションフレームワーク:Flask app = Flask(__name__) q = Queue(connection=conn) result = q.enqueue(perfect_order_USDJPY, ) #環境変数からLINE Access Tokenを設定 LINE_CHANNEL_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"] #環境変数からLINE Channel Secretを設定 LINE_CHANNEL_SECRET = os.environ["LINE_CHANNEL_SECRET"] line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN) handler = WebhookHandler(LINE_CHANNEL_SECRET) @app.route("/callback", methods=['POST']) def callback(): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # handle webhook body try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK' # MessageEvent @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text='暇人か!') ) #perfect order message def message1(): try: line_bot_api.push_message('<to>', TextSendMessage(text="ドル円でパーフェクトオーダーが発生しました。")) except LineBotApiError: # error handle print("Error occurred") if __name__ == "__main__": port = int(os.getenv("PORT", 5000)) app.run(host="0.0.0.0", port=port)
task.py
#import library import pandas as pd import numpy as np from oandapyV20 import API from oandapyV20.exceptions import V20Error from oandapyV20.endpoints.pricing import PricingStream import oandapyV20.endpoints.instruments as instruments import time import schedule import datetime accountID = "アカウントID" access_token = "アクセストークン" def perfect_order_USDJPY(): def get_data(): api = API(access_token=access_token, environment="practice") #15分足2000個 params = { "count": 2000, "granularity": "M15" } #ドル円ローソク足データ取得 r = instruments.InstrumentsCandles(instrument="USD_JPY", params=params) api.request(r) # ストリーミングの最初の1件目のデータを確認 r.response['candles'][0] rate = pd.DataFrame.from_dict({r.response['candles'][i]['time']: r.response['candles'][i]['mid'] for i in range(0,len(r.response['candles'])) for j in r.response['candles'][i]['mid'].keys()}, orient='index', ) rate.index = pd.to_datetime(rate.index) #EMAを求める ema = pd.DataFrame() ema['ema_10'] = rate['c'].ewm(span=10).mean() ema['ema_20'] = rate['c'].ewm(span=20).mean() ema['ema_40'] = rate['c'].ewm(span=40).mean() ema['ema_80'] = rate['c'].ewm(span=80).mean() ema['ema_320'] = rate['c'].ewm(span=320).mean() #計算したデータはseries型なのでデータフレームにする。そうしないとエラーが出るから。 ema_10 = pd.DataFrame(ema['ema_10']).reset_index() ema_20 = pd.DataFrame(ema['ema_20']).reset_index() ema_40 = pd.DataFrame(ema['ema_40']).reset_index() ema_80 = pd.DataFrame(ema['ema_80']).reset_index() ema_320 = pd.DataFrame(ema['ema_320']).reset_index() return ema_10, ema_20, ema_40, ema_80, ema_320 scheduler1 = schedule.Scheduler() scheduler2 = schedule.Scheduler() def perfect_order_classifier(message): ema_10, ema_20, ema_40, ema_80, ema_320 = get_data() #10分前がパーフェクトオーダーじゃない場合今がパーフェクトオーダーかどうか計算する if not ema_10.iat[-2, -1] < ema_20.iat[-2, -1] < ema_40.iat[-2, -1] < ema_80.iat[-2, -1] < ema_320.iat[-2, -1] \ and not ema_10.iat[-2, -1] > ema_20.iat[-2, -1] > ema_40.iat[-2, -1] > ema_80.iat[-2, -1] > ema_320.iat[-2, -1]: #下降トレンド if ema_10.iat[-1, -1] < ema_20.iat[-1, -1] < ema_40.iat[-1, -1] < ema_80.iat[-1, -1] < ema_320.iat[-1, -1]: print("Perfect Order Now") #ここからmessage1関数を呼び出したい #上昇トレンド elif ema_10.iat[-1, -1] > ema_20.iat[-1, -1] > ema_40.iat[-1, -1] > ema_80.iat[-1, -1] > ema_320.iat[-1, -1]: print("Perfect Order Now") #ここからmessage1関数を呼び出したい else: print("Not Perfect Order") else: pass #10分毎にperfect_order_classifierを実行する scheduler1.every(10).minutes.do(perfect_order_classifier, "メッセージを送れ") def timer(): day_of_the_week = ["月","火","水","木","金","土","日"] get_today = datetime.date.today() #今日の日付を取得 weekday = day_of_the_week[get_today.weekday()] #曜日に変換 if weekday == "土" or weekday == "日": pass else: #機能 while True: now = datetime.datetime.now() if now.hour == 23 and now.minute >= 50: break else: scheduler1.run_pending() time.sleep(1) #timer()を毎日0時に実行する scheduler2.every().day.at("00:00").do(timer) while True: scheduler2.run_pending() time.sleep(1)
worker.py
import os import redis from rq import Worker, Queue, Connection listen = ['high', 'default', 'low'] redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379') conn = redis.from_url(redis_url) if __name__ == '__main__': with Connection(conn): worker = Worker(map(Queue, listen)) worker.work()
試したこと
ここに問題に対して試したことを記載してください。
補足情報(FW/ツールのバージョンなど)
ここにより詳細な情報を記載してください。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。