持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情
前言
昨天的文章中,程序通过python flask + uwsgi + nginx进行了打包,并且通过postman调用接口能够正常返回数据,但是发现有几个问题,首先,match (n) return n limit 25 或者 limit n 返回的数据节点数量无法把控,比如根节点返回三个,子节点返回n-3个,但是实际我们需要多个根节点,这种情况并不适配,此外如果叶子节点数目过于庞大的话,会导致根节点数目进一步变少,并且关系不全,随着数目越多问题约凸显。此外,数据量的增加会导致查询效率低下,图数据库返回大量节点耗时较长,因此对于首页这种经常刷新的网页,并不适合从数据库中查询,基于以上问题,寻求解决办法。
引入Redis
之前的文章中提到过redis,作为缓存数据库,其将数据加载于内存,查询效率较高,并且IO时间较小,因此十分适合首页等数据经常刷新的地方,对于数据更新,可以在flask程序中起定时任务,按照时间间隔进行数据刷新操作,基于以上想法,在程序中引入redis部分。
redis.ini
[REDIS]
REMOTE_HOST=127.0.0.1
REMOTE_PORT=26379
PASSWORD=m0jXXXXXXXwsx3xxm3vnrWp%234wp
redis配置文件,其中第一行为IP地址,第二行为端口,第三行为密码
redisUtils.py
from configparser import RawConfigParser
import redis
class redisUtils():
def __init__(self):
self.getMysqlCfg()
self.getRedisConnect()
def getMysqlCfg(self):
config = RawConfigParser()
# 获取配置文件的真实路径
path = r"config/redis.ini"
config.read(path, encoding="utf-8")
self.REMOTE_HOST = config.get("REDIS","REMOTE_HOST")
self.REMOTE_PORT = int(config.get("REDIS","REMOTE_PORT"))
self.PASSWORD = config.get("REDIS","PASSWORD")
def getRedisConnect(self):
self.redisConn = redis.Redis(host=self.REMOTE_HOST, port=self.REMOTE_PORT, db=0, password=self.PASSWORD)
def getKey(self, key):
if self.redisConn.exists(key):
return self.redisConn.get(key).decode("utf-8")
else:
return None
def setKey(self, key, value):
self.redisConn.set(key, value)
def delKey(self, key):
self.redisConn.delete(key)
def setKeyWithTime(self, key, value, time):
self.redisConn.set(key, value, ex=time)
首先获取配置文件,根据配置文件完成redis的连接操作,通过getRedisConnect函数,其余为类的一些方法,与getkey()获取关键字对应的值,setkey()往数据库进行插入操作,delkey() redis的删除操作,最后一个是配有设置过期时间的关键字插入方法。
node2redis.py
from py2neo.data import Node
# {"company_name":{"product_name":["cvenumber1","cvenumber1","cvenumber1"]}}}
# vulnerability = [{company_name:[{product_name:[cve_number]}]}]
from Utils.connectUtils import connect2Neo4J
from Utils.redisUtils import redisUtils
def getData():
conn = connect2Neo4J().conn
vulnerability = []
conpany_list = conn.run("MATCH (n:company)-[r]->() RETURN n,count(r) order by count(r) DESC limit 50")
for index, item in enumerate(conpany_list):
print(index,item)
conpany_dic = {}
print(item.data())
conpany_name = (str(item.data().get("n")).split("name: '")[1].rstrip("'})"))
product_number = int(item.data().get("count(r)"))
print(product_number)
conpany_dic[conpany_name] = []
conpany_dic["product_number"] = product_number
product_list = conn.run(f"MATCH (n:company{{name:'{conpany_name}'}})-[r]->(m:product) -[t]-> () RETURN m,count(t) order by count(t) DESC limit 50")
product_list_all = []
for item in product_list:
product_dic = {}
product_name = (str(item.data().get("m")).split("name: '")[1].rstrip("'})"))
cvenode_number = int(item.data().get("count(t)"))
print(cvenode_number)
cve_list = conn.run(f"MATCH (n:company{{name:'{conpany_name}'}})-[r]->(m:product{{name:'{product_name}'}}) -[t]-> (o:cvenumberNode) RETURN o order by o.name DESC limit 50")
cve_list_all = []
for item in cve_list:
cve_number = (str(item.data().get("o")).split("name: '")[1].rstrip("'})"))
cve_list_all.append(cve_number)
product_dic[product_name] = cve_list_all
product_dic["cve_number"] = cvenode_number
product_list_all.append(product_dic)
conpany_dic[conpany_name].append(product_list_all)
vulnerability.append(conpany_dic)
print(vulnerability)
redisUtils().setKey("vulnerability",str(vulnerability))
if __name__ == '__main__':
getData()
基本逻辑是将图数据库保存为如下格式:
vulnerability = [{company_name:[{product_name:[cve_number]}]}]
每一个labels对应的值都以list进行存储,这样就能够对数据进行split操作,根据limit值返回对应的数据量,这样能够保证数据的雨露均沾,此外,数据也经过了排序,对于公司,则根据公司产品数目进行排序,对于产品则根据漏洞数量进行排序,对于漏洞,则根据漏洞名称【时间先后进行排序】,对于格式化后的数据,存储每个厂商对应产品对应漏洞的键值对,键为“vulnerability”,这样通过查询接口对数据进行查询,速度由于通过neo4J进行查询,并且数据分布较为均匀。
明天将对redis进行优化,并且准备模糊查询的方法,(#^.^#)~