Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redis添加会话管理支持 #2816

Merged
merged 33 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6ca8e7f
添加favicon图片
May 9, 2024
870ea4e
Merge branch 'master' of https://github.com/hhyo/Archery
May 10, 2024
d24784d
Merge branch 'master' of https://github.com/hhyo/Archery
Jun 20, 2024
e80b335
firset
Jun 20, 2024
e4d02bb
修改
Jun 20, 2024
118504c
撤销
Jun 20, 2024
a4531d0
Merge branch 'master' of https://github.com/hhyo/Archery
Jun 21, 2024
7c2fea5
Merge branch 'master' of https://github.com/hhyo/Archery
Jun 27, 2024
64abe26
Merge branch 'master' of https://github.com/hhyo/Archery
Jul 31, 2024
bf3f327
Merge branch 'master' of https://github.com/hhyo/Archery
Aug 7, 2024
d23edba
Merge branch 'master' of https://github.com/hhyo/Archery
Aug 7, 2024
405b79e
Merge branch 'master' of https://github.com/hhyo/Archery
Aug 14, 2024
36bdde6
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 16, 2024
75edaf7
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 16, 2024
44230c0
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 19, 2024
d9bea16
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 20, 2024
09834da
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 28, 2024
aee2867
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Aug 30, 2024
dd0c01c
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Sep 2, 2024
a769d28
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Sep 19, 2024
c4734b7
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Sep 19, 2024
4246c62
修改 ssl及是否验证证书
feiazifeiazi Sep 19, 2024
913d380
is_ssl使用实例的字段
feiazifeiazi Sep 19, 2024
bc5e992
redis添加会话管理
feiazifeiazi Sep 19, 2024
e6a9753
all
feiazifeiazi Sep 19, 2024
1598adc
Merge branch 'master' of https://github.com/hhyo/Archery
feiazifeiazi Sep 19, 2024
5031be8
cicd
feiazifeiazi Sep 19, 2024
ed135b9
Merge branch 'master' of https://github.com/feiazifeiazi/Archery_Genu…
feiazifeiazi Sep 19, 2024
e8f1297
processlist方法已提升为父类方法
feiazifeiazi Sep 20, 2024
a0dbfd7
会话管理-支持的数据库类型
feiazifeiazi Sep 20, 2024
38a39dd
测试 processlist 方法,模拟获取连接并返回客户端列
feiazifeiazi Sep 20, 2024
7808b84
修改一下参数名request_kwargs
feiazifeiazi Sep 20, 2024
ab3981f
修改一下参数名request_kwargs
feiazifeiazi Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions sql/db_diagnostic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
def process(request):
instance_name = request.POST.get("instance_name")
command_type = request.POST.get("command_type")
request_kwargs = {
key: value for key, value in request.POST.items() if key != "command_type"
}

try:
instance = user_instances(request.user).get(instance_name=instance_name)
Expand All @@ -31,21 +34,8 @@ def process(request):

query_engine = get_engine(instance=instance)
query_result = None
if instance.db_type == "mysql":
query_result = query_engine.processlist(command_type)

elif instance.db_type == "mongo":
query_result = query_engine.current_op(command_type)
elif instance.db_type == "oracle":
query_result = query_engine.session_list(command_type)
else:
result = {
"status": 1,
"msg": "暂时不支持{}类型数据库的进程列表查询".format(instance.db_type),
"data": [],
}
return HttpResponse(json.dumps(result), content_type="application/json")

# processlist方法已提升为父类方法,简化此处的逻辑。进程添加新数据库支持时,改前端即可。
query_result = query_engine.processlist(command_type=command_type, **request_kwargs)
if query_result:
if not query_result.error:
processlist = query_result.to_dict()
Expand Down
4 changes: 4 additions & 0 deletions sql/engines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def server_version(self):
"""返回引擎服务器版本,返回对象为tuple (x,y,z)"""
return tuple()

def processlist(self, command_type, **kwargs) -> ResultSet:
"""获取连接信息"""
return ResultSet()

def kill_connection(self, thread_id):
"""终止数据库连接"""

Expand Down
2 changes: 1 addition & 1 deletion sql/engines/cloud/aliyun_rds.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, instance=None):
self.instance_name = instance.instance_name

# 将sql/aliyun_rds.py的函数迁移值此
def processlist(self, command_type):
def processlist(self, command_type, **kwargs):
if command_type is None or command_type == "":
command_type = "Query"

Expand Down
2 changes: 1 addition & 1 deletion sql/engines/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,7 @@ def fill_query_columns(cursor, columns):
cols.append(key)
return cols

def current_op(self, command_type):
def processlist(self, command_type, **kwargs):
"""
获取当前连接信息

Expand Down
2 changes: 1 addition & 1 deletion sql/engines/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ def osc_control(self, **kwargs):
"""
return self.inc_engine.osc_control(**kwargs)

def processlist(self, command_type):
def processlist(self, command_type, **kwargs):
"""获取连接信息"""
base_sql = "select id, user, host, db, command, time, state, ifnull(info,'') as info from information_schema.processlist"
# escape
Expand Down
2 changes: 1 addition & 1 deletion sql/engines/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1451,7 +1451,7 @@ def execute(self, db_name=None, sql="", close_conn=True, parameters=None):
self.close()
return result

def session_list(self, command_type):
def processlist(self, command_type, **kwargs):
"""获取会话信息"""
base_sql = """select
s.sid,
Expand Down
15 changes: 15 additions & 0 deletions sql/engines/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ def query_check(self, db_name=None, sql="", limit_num=0):
result["msg"] = "禁止执行该命令!"
return result

def processlist(self, command_type, **kwargs):
"""获取连接信息"""
sql = "client list"
result_set = ResultSet(full_sql=sql)
conn = self.get_connection(db_name=0)
clients = conn.client_list()
# 根据空闲时间排序
sort_by = "idle"
reverse = False
clients = sorted(
clients, key=lambda client: client.get(sort_by), reverse=reverse
)
result_set.rows = clients
return result_set

def query(self, db_name=None, sql="", limit_num=0, close_conn=True, **kwargs):
"""返回 ResultSet"""
result_set = ResultSet(full_sql=sql)
Expand Down
45 changes: 41 additions & 4 deletions sql/engines/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,43 @@ def test_execute_workflow_success(self, _execute_command):
self.assertIsInstance(execute_result, ReviewSet)
self.assertEqual(execute_result.rows[0].__dict__.keys(), row.__dict__.keys())

@patch("sql.engines.redis.RedisEngine.get_connection")
def test_processlist(self, mock_get_connection):
"""测试 processlist 方法,模拟获取连接并返回客户端列表"""

# 模拟 Redis 连接的客户端列表
mock_conn = Mock()

return_value_mock = [
{"id": "1", "idle": 10, "name": "client_1"},
{"id": "2", "idle": 5, "name": "client_2"},
{"id": "3", "idle": 20, "name": "client_3"},
]
mock_conn.client_list.return_value = return_value_mock

# 设置 get_connection 返回模拟连接
mock_get_connection.return_value = mock_conn

# 创建 RedisEngine 实例
new_engine = RedisEngine(instance=self.ins)

# 调用 processlist 方法并测试其返回值
command_types = ["All"] # 假设支持的命令类型
for command_type in command_types:
result_set = new_engine.processlist(command_type=command_type)

# 验证返回值是 ResultSet 实例
self.assertIsInstance(result_set, ResultSet)

# 验证返回的客户端列表被正确排序
sorted_clients = sorted(
return_value_mock, key=lambda client: client.get("idle"), reverse=False
)
self.assertEqual(result_set.rows, sorted_clients)

# 验证 get_connection 是否被调用
mock_get_connection.assert_called()


class TestPgSQL(TestCase):
@classmethod
Expand Down Expand Up @@ -1497,11 +1534,11 @@ def test_execute(self, _connect, _cursor, _execute):
self.assertIsInstance(execute_result, ResultSet)

@patch("sql.engines.oracle.OracleEngine.query")
def test_session_list(self, _query):
def test_processlist(self, _query):
new_engine = OracleEngine(instance=self.ins)
_query.return_value = ResultSet()
for command_type in ["All", "Active", "Others"]:
r = new_engine.session_list(command_type)
r = new_engine.processlist(command_type)
self.assertIsInstance(r, ResultSet)

@patch("sql.engines.oracle.OracleEngine.query")
Expand Down Expand Up @@ -1803,7 +1840,7 @@ def test_fill_query_columns(self):
self.assertEqual(cols, ["_id", "title", "tags", "likes", "text", "author"])

@patch("sql.engines.mongo.MongoEngine.get_connection")
def test_current_op(self, mock_get_connection):
def test_processlist(self, mock_get_connection):
class Aggregate:
def __enter__(self):
yield {"client": "single_client"}
Expand All @@ -1817,7 +1854,7 @@ def __exit__(self, *arg, **kwargs):
mock_get_connection.return_value = mock_conn
command_types = ["Full", "All", "Inner", "Active"]
for command_type in command_types:
result_set = self.engine.current_op(command_type)
result_set = self.engine.processlist(command_type)
self.assertIsInstance(result_set, ResultSet)

@patch("sql.engines.mongo.MongoEngine.get_connection")
Expand Down
127 changes: 114 additions & 13 deletions sql/templates/dbdiagnostic.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<optgroup id="optgroup-mysql" label="MySQL"></optgroup>
<optgroup id="optgroup-mongo" label="MongoDB"></optgroup>
<optgroup id="optgroup-oracle" label="Oracle"></optgroup>
<optgroup id="optgroup-redis" label="Redis"></optgroup>
</select>
</div>
<div id="command-div" class="form-group">
Expand Down Expand Up @@ -326,6 +327,111 @@ <h4 class="modal-title text-danger">确定要终止所选会话吗?</h4>
return html.join('');
}
]
,[
'redis',
["All"],
[{
title: '', // 用于多选框
field: 'checkbox',
checkbox: true
}, {
title: 'Id',
field: 'id',
sortable: true
}, {
title: '远程地址',
field: 'addr',
sortable: true
}, {
title: '本地地址',
field: 'laddr',
sortable: true
}, {
title: '客户端名称',
field: 'name',
sortable: true
}, {
title: '用户',
field: 'user',
sortable: true
},
{
title: '数据库',
field: 'db',
sortable: true
}, {
title: '连接耗时(秒)',
field: 'age',
sortable: true
}, {
title: '空闲时间(秒)',
field: 'idle',
sortable: true
}, {
title: '命令',
field: 'cmd',
sortable: true
}, {
title: '总内存',
field: 'tot-mem',
sortable: true
},{
title: '输出内存',
field: 'omem',
sortable: true
}, {
title: '标志',
field: 'flags',
sortable: true
},{
title: '文件描述符',
field: 'fd',
sortable: true
},{
title: '订阅数',
field: 'sub',
sortable: true
}, {
title: '模式订阅数',
field: 'psub',
sortable: true
}, {
title: 'MULTI 队列长度',
field: 'multi',
sortable: true
}, {
title: '查询缓冲区',
field: 'qbuf',
sortable: true
}, {
title: '查询缓冲区空闲',
field: 'qbuf-free',
sortable: true
}, {
title: '参数内存',
field: 'argv-mem',
sortable: true
}, {
title: '输出缓冲区长度',
field: 'obl',
sortable: true
}, {
title: '输出链长度',
field: 'oll',
sortable: true
}, {
title: '事件文件',
field: 'events',
sortable: true
}, {
title: '重定向',
field: 'redir',
sortable: true
}],
function (index, row) {
var html = [];
}
]
]


Expand Down Expand Up @@ -931,12 +1037,14 @@ <h4 class="modal-title text-danger">确定要终止所选会话吗?</h4>
$(document).ready(function () {
//获取用户实例列表
$(function () {
// 会话管理-支持的数据库类型
supportedDbType=['mysql','mongo', 'oracle','redis']
$.ajax({
type: "get",
url: "/group/user_all_instances/",
dataType: "json",
data: {
db_type: ['mysql','mongo', 'oracle']
db_type: supportedDbType
},
complete: function () {
//如果已选择instance_name,进入页面自动填充,并且重置激活id
Expand All @@ -951,20 +1059,13 @@ <h4 class="modal-title text-danger">确定要终止所选会话吗?</h4>
if (data.status === 0) {
let result = data['data'];
allInstances = result;
// $("#instance_name").empty();
$("#optgroup-mysql").empty();
$("#optgroup-mongo").empty();
$("#optgroup-oracle").empty();
supportedDbType.forEach(function(db) {
$("#optgroup-" + db).empty();
});
for (let i = 0; i < result.length; i++) {
let instance = "<option value=\"" + result[i]['instance_name'] + "\">" + result[i]['instance_name'] + "</option>";
// $("#instance_name").append(instance);
if (result[i]['db_type'] === 'mysql') {
$("#optgroup-mysql").append(instance);
} else if (result[i]['db_type'] === 'mongo') {
$("#optgroup-mongo").append(instance);
} else if (result[i]['db_type'] === 'oracle') {
$("#optgroup-oracle").append(instance);
}
var dbType = result[i]['db_type'];
$("#optgroup-" + dbType).append(instance);
}
$('#instance_name').selectpicker('render');
$('#instance_name').selectpicker('refresh');
Expand Down
Loading