def con_es(self, is_auth=False): """ :param is_auth: 用户名密码状态 :return: es_obj """ # 读取mapping mapping_json_path = 'config/NewsESMapping.json' with open(os.path.join(BASE_DIR, mapping_json_path)) as f: mapping = json.load(f) try: if is_auth: es = Elasticsearch( hosts=ES_HOST_PORT, # 主机 端口 http_auth=('elastic', 'password'), # 认证 http_compress=True # 压缩 ) else: es = Elasticsearch(hosts=ES_HOST_PORT, http_compress=True) except: traceback.print_exc() globalLog.error(traceback.format_exc()) else: self.es = es # 创建索引 忽视创建重复400状态码 es.indices.create(index=ES_INDEX, body=mapping, ignore=[400]) return es
def callback(channel, method, properties, message): try: print('[*] Waiting for logs. To exit press CTRL+C') msg = pickle.loads(message) print("消息内容:{}".format(msg)) return Processing(msg).process_data(channel, method) except: traceback.print_exc() globalLog.error(traceback.format_exc()) channel.basic_nack(delivery_tag=method.delivery_tag, requeue=True) channel.close(reply_text='callback func running Error')
def exchange_aliases(self, index_name, alias_name): """ 为特定索引创建别名 :param index_name: 索引名 :param alias_name: 别名 """ if self.es: self.es.indices.put_alias(index=[index_name], name=alias_name) else: globalLog.error(traceback.format_exc()) raise Exception('not es object')
def con_redis(): # decode_responses 将返回数据不是二级制byte redis_cli = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWORD) try: redis_cli.ping() except: traceback.print_exc() globalLog.error(traceback.format_exc()) else: return redis_cli
def con_mongo(collection_name=None): """ :param collection_name: mongo集合名 :return: mongo游标 """ try: mg_cli = pymongo.MongoClient(MONGO_URL) mg_db = mg_cli[MONGO_DB] mg_cursor = mg_db[collection_name] except: traceback.print_exc() globalLog.error(traceback.format_exc()) else: return mg_cursor
def process_data(self, channel=None, method=None): """ 采用多线程分布式数据处理 => callback 主线程: 限时/主动发送心跳操作 子线程: 处理数据方法 :param channel: 信道 :param method: mq方法 """ thread_process = MyThread(self.thread_process_data, args=(channel, method)) thread_process.setDaemon(True) thread_process.start() # 限制任务时间10分钟 超过就把次消息拒绝 for i in range(1, 61 * 10): time.sleep(1) result = thread_process.get_result() # 每10s消费者主动发送一次心跳检测 if i % 30 == 0: # 将确保处理数据方法完整执行完成 # 防止server端主动提出分手 => 认为消费者死掉 channel.connection.process_data_events() # 子线程任务处理完 => true if result is True: # 记录完成消息数据内容 globalLog.success("消息内容:{} 消息处理总量:{}条".format( self.msg, self.mongo_count)) return result # 子线程任务报错 => error 或者 thread_process_data 返回 False elif result == 'ERROR': globalLog.error("处理报错消息内容:{} 消息处理总量:{}条".format( self.msg, self.mongo_count)) return 'ERROR' elif result is False: globalLog.error("处理报错消息内容:{} 消息处理总量:{}条".format( self.msg, self.mongo_count)) return False else: globalLog.warning("处理超时消息内容:{} 消息处理总量:{}条".format( self.msg, self.mongo_count)) # 拒绝此条消息 放入死信队列 channel.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
def listening(self, queue_name='default_queue', exchange_name='default_exchange', routing_key='default_route', callback=None): """ 监听队列里的消息, 优先启动 """ # 创建exchange并设置类型 self.channel.exchange_declare( exchange=exchange_name, exchange_type='direct', durable=True ) print("消费者连接MQ成功") # 指定死信队列配置 创建死信队列脚本 path: mq/DLXQueueScript.py arguments = {"x-dead-letter-exchange": "dlx.financial_news", "x-dead-letter-routing-key": "dlx"} # durable 持久化 # auto_delete 是否自动删除queue 当还一个消费者断开连接 # 切换到指定的队列中,如果队列不存在,则创建 self.channel.queue_declare( queue=queue_name, durable=True, exclusive=False, auto_delete=False, arguments=arguments ) # 绑定交换器和队列 self.channel.queue_bind( exchange=exchange_name, routing_key=routing_key, queue=queue_name ) # 在处理并确认上一条消息之前,不要将新消息发送给消费者 # 而是将其分派给不忙的下一个工作程序 防止消息积压 self.channel.basic_qos(prefetch_count=1) # Queue每次给每个消费者发送一条消息 if not callback: callback = self.__callback # 调用回调函数处理消息数据 # no_ack=False 设置为消息处理完毕后,消费者必须明确告知rabbitMQ server已经处理完毕 # 否则rabbitMQ server将视为消息处理失败,把该消息重新放回到队列当中 self.channel.basic_consume(queue='financial_news', on_message_callback=callback, auto_ack=False) try: # 消费者阻塞监听 self.channel.start_consuming() except: traceback.print_exc() globalLog.error(traceback.format_exc())
def con_mysql(sql, con): """ 通过sqlalchemy连接数据库 :param sql: sql查询语句 :param con: mysql_url 连接地址 ==>str :return: 数据 """ try: conn = create_engine(con) cursor = conn.execute(sql) result = cursor.fetchall() except: traceback.print_exc() globalLog.error(traceback.format_exc()) else: # 查询数据长度1时直接从列表中获取元组中获取数据 # 例: [(423507,)] if len(result) == 1: return result[0][0] else: return result
def thread_process_data(self, channel=None, method=None): """ 子线程执行处理数据任务 :param channel: 信道 :param method: mq方法 可以获取任务id :return: 是否执行ok """ try: # 处理数据量 data = self.get_mongo() # 检测模型是否开启 if self.mt_model is False: # 拒绝传入的消息。此方法允许客户端拒绝消息 # 如果requeue为true,则服务器将尝试重新排队该消息。 # 如果requeue为false或requeue尝试失败,则消息将被丢弃或置为死信。 channel.basic_reject(delivery_tag=method.delivery_tag, requeue=True) globalLog.error('算法模型服务端口未开启') print('算法模型服务端口未开启, 消息以重新队列消息') return 'ERROR' # 通过缓存获取抓撞库数据 data_df = self.redis_cache() es_actions = [] sql_actions = [] for n, mongo_data in enumerate(data, start=1): contents = ''.join(mongo_data.get('contents')) # 数据处理的内容 # 1. 调用模型提取公司名称 ==> list cnm = GetCompanyNameModel() # # TODO 旧模型 # client1_list = cnm.get_api(text=contents, client_num=cnm.bclient_1) # client2_list = cnm.get_api(text=contents, client_num=cnm.bclient_2) # model_company_list = client1_list + client2_list # TODO 新模型 需要将全角转成半角 model_company_list = cnm.get_api(text=contents, client_num=cnm.bclient_3) # 2. 调用撞库 ==> list hit_company_list = self.hit_db(db_data=data_df, text=contents) # 3. 拼接两个方法返回值 ==> list company_list = list(set(model_company_list + hit_company_list)) # TODO 模型调用获取APP并找到公司名称 # TODO 所有公司名称需要过滤别名库获取公司全称 # 拼装数据 ES Mysql action_data, sql_data = self.format_data( mongo_data, company_list) es_actions.append(action_data) sql_actions.append(sql_data) # 保存数据库 self.save_sql(sql_actions) # [Mysql] self.save_es(es_actions) # [ES] except: # 数据处理报错记录并返回error traceback.print_exc() globalLog.error(traceback.format_exc()) return 'ERROR' else: return True