Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
b00fb8c1
T
TDengine
项目概览
taosdata
/
TDengine
大约 2 年 前同步成功
通知
1192
Star
22018
Fork
4786
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
TDengine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
b00fb8c1
编写于
11月 26, 2020
作者:
dengyihao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
reduce client mem cost
上级
5a207951
变更
8
显示空白变更内容
内联
并排
Showing
8 changed file
with
153 addition
and
155 deletion
+153
-155
src/client/inc/tsclient.h
src/client/inc/tsclient.h
+3
-3
src/client/src/tscLocal.c
src/client/src/tscLocal.c
+5
-2
src/client/src/tscServer.c
src/client/src/tscServer.c
+105
-119
src/client/src/tscSql.c
src/client/src/tscSql.c
+7
-14
src/client/src/tscSub.c
src/client/src/tscSub.c
+3
-2
src/client/src/tscSubquery.c
src/client/src/tscSubquery.c
+1
-0
src/client/src/tscSystem.c
src/client/src/tscSystem.c
+11
-5
src/client/src/tscUtil.c
src/client/src/tscUtil.c
+18
-10
未找到文件。
src/client/inc/tsclient.h
浏览文件 @
b00fb8c1
...
...
@@ -331,7 +331,7 @@ typedef struct STscObj {
char
superAuth
:
1
;
uint32_t
connId
;
uint64_t
rid
;
// ref ID returned by taosAddRef
struct
SSqlObj
*
pHb
;
int64_t
hbrid
;
struct
SSqlObj
*
sqlList
;
struct
SSqlStream
*
streamList
;
void
*
pDnodeConn
;
...
...
@@ -371,7 +371,7 @@ typedef struct SSqlObj {
struct
SSqlObj
**
pSubs
;
struct
SSqlObj
*
prev
,
*
next
;
struct
SSqlObj
**
self
;
int64_t
self
;
}
SSqlObj
;
typedef
struct
SSqlStream
{
...
...
@@ -504,7 +504,7 @@ static FORCE_INLINE void tscGetResultColumnChr(SSqlRes* pRes, SFieldInfo* pField
}
extern
SCacheObj
*
tscMetaCache
;
extern
SCacheObj
*
tscObjCache
;
extern
int
tscObjRef
;
extern
void
*
tscTmr
;
extern
void
*
tscQhandle
;
extern
int
tscKeepConn
[];
...
...
src/client/src/tscLocal.c
浏览文件 @
b00fb8c1
...
...
@@ -814,8 +814,11 @@ static int32_t tscProcessClientVer(SSqlObj *pSql) {
static
int32_t
tscProcessServStatus
(
SSqlObj
*
pSql
)
{
STscObj
*
pObj
=
pSql
->
pTscObj
;
if
(
pObj
->
pHb
!=
NULL
)
{
if
(
pObj
->
pHb
->
res
.
code
==
TSDB_CODE_RPC_NETWORK_UNAVAIL
)
{
SSqlObj
*
pHb
=
(
SSqlObj
*
)
taosAcquireRef
(
tscObjRef
,
pObj
->
hbrid
);
if
(
pHb
!=
NULL
)
{
int32_t
code
=
pHb
->
res
.
code
;
taosReleaseRef
(
tscObjRef
,
pObj
->
hbrid
);
if
(
code
==
TSDB_CODE_RPC_NETWORK_UNAVAIL
)
{
pSql
->
res
.
code
=
TSDB_CODE_RPC_NETWORK_UNAVAIL
;
return
pSql
->
res
.
code
;
}
...
...
src/client/src/tscServer.c
浏览文件 @
b00fb8c1
...
...
@@ -175,10 +175,10 @@ void tscProcessHeartBeatRsp(void *param, TAOS_RES *tres, int code) {
if
(
pRsp
->
streamId
)
tscKillStream
(
pObj
,
htonl
(
pRsp
->
streamId
));
}
}
else
{
tscDebug
(
"%
p heartbeat failed, code:%s"
,
pObj
->
pHb
,
tstrerror
(
code
));
tscDebug
(
"%
"
PRId64
" heartbeat failed, code:%s"
,
pObj
->
hbrid
,
tstrerror
(
code
));
}
if
(
pObj
->
pHb
!=
NULL
)
{
if
(
pObj
->
hbrid
!=
0
)
{
int32_t
waitingDuring
=
tsShellActivityTimer
*
500
;
tscDebug
(
"%p send heartbeat in %dms"
,
pSql
,
waitingDuring
);
...
...
@@ -193,21 +193,11 @@ void tscProcessActivityTimer(void *handle, void *tmrId) {
STscObj
*
pObj
=
taosAcquireRef
(
tscRefId
,
rid
);
if
(
pObj
==
NULL
)
return
;
SSqlObj
*
pHB
=
pObj
->
pHb
;
void
**
p
=
taosCacheAcquireByKey
(
tscObjCache
,
&
pHB
,
sizeof
(
TSDB_CACHE_PTR_TYPE
));
if
(
p
==
NULL
)
{
tscWarn
(
"%p HB object has been released already"
,
pHB
);
taosReleaseRef
(
tscRefId
,
pObj
->
rid
);
return
;
}
assert
(
*
pHB
->
self
==
pHB
);
SSqlObj
*
pHB
=
taosAcquireRef
(
tscObjRef
,
pObj
->
hbrid
);
assert
(
pHB
->
self
==
pObj
->
hbrid
);
pHB
->
retry
=
0
;
int32_t
code
=
tscProcessSql
(
pHB
);
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
false
);
taosReleaseRef
(
tscObjRef
,
pObj
->
hbrid
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
tscError
(
"%p failed to sent HB to server, reason:%s"
,
pHB
,
tstrerror
(
code
));
}
...
...
@@ -236,7 +226,7 @@ int tscSendMsgToServer(SSqlObj *pSql) {
.
msgType
=
pSql
->
cmd
.
msgType
,
.
pCont
=
pMsg
,
.
contLen
=
pSql
->
cmd
.
payloadLen
,
.
ahandle
=
pSql
,
.
ahandle
=
(
void
*
)
pSql
->
self
,
.
handle
=
NULL
,
.
code
=
0
};
...
...
@@ -251,26 +241,25 @@ int tscSendMsgToServer(SSqlObj *pSql) {
void
tscProcessMsgFromServer
(
SRpcMsg
*
rpcMsg
,
SRpcEpSet
*
pEpSet
)
{
TSDB_CACHE_PTR_TYPE
handle
=
(
TSDB_CACHE_PTR_TYPE
)
rpcMsg
->
ahandle
;
void
**
p
=
taosCacheAcquireByKey
(
tscObjCache
,
&
handle
,
sizeof
(
TSDB_CACHE_PTR_TYPE
)
);
if
(
p
==
NULL
)
{
SSqlObj
*
pSql
=
(
SSqlObj
*
)
taosAcquireRef
(
tscObjRef
,
handle
);
if
(
p
Sql
==
NULL
)
{
rpcFreeCont
(
rpcMsg
->
pCont
);
return
;
}
SSqlObj
*
pSql
=
*
p
;
assert
(
pSql
!=
NULL
);
assert
(
pSql
->
self
==
handle
);
STscObj
*
pObj
=
pSql
->
pTscObj
;
SSqlRes
*
pRes
=
&
pSql
->
res
;
SSqlCmd
*
pCmd
=
&
pSql
->
cmd
;
assert
(
*
pSql
->
self
==
pSql
);
assert
(
pSql
->
self
==
handle
);
pSql
->
rpcRid
=
-
1
;
if
(
pObj
->
signature
!=
pObj
)
{
tscDebug
(
"%p DB connection is closed, cmd:%d pObj:%p signature:%p"
,
pSql
,
pCmd
->
command
,
pObj
,
pObj
->
signature
);
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
true
);
taosRemoveRef
(
tscObjRef
,
pSql
->
self
);
taosReleaseRef
(
tscObjRef
,
pSql
->
self
);
rpcFreeCont
(
rpcMsg
->
pCont
);
return
;
}
...
...
@@ -280,10 +269,8 @@ void tscProcessMsgFromServer(SRpcMsg *rpcMsg, SRpcEpSet *pEpSet) {
tscDebug
(
"%p sqlObj needs to be released or DB connection is closed, cmd:%d type:%d, pObj:%p signature:%p"
,
pSql
,
pCmd
->
command
,
pQueryInfo
->
type
,
pObj
,
pObj
->
signature
);
void
**
p1
=
p
;
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p1
,
false
);
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
true
);
taosRemoveRef
(
tscObjRef
,
pSql
->
self
);
taosReleaseRef
(
tscObjRef
,
pSql
->
self
);
rpcFreeCont
(
rpcMsg
->
pCont
);
return
;
}
...
...
@@ -326,7 +313,7 @@ void tscProcessMsgFromServer(SRpcMsg *rpcMsg, SRpcEpSet *pEpSet) {
// if there is an error occurring, proceed to the following error handling procedure.
if
(
rpcMsg
->
code
==
TSDB_CODE_TSC_ACTION_IN_PROGRESS
)
{
taos
CacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
false
);
taos
ReleaseRef
(
tscObjRef
,
pSql
->
self
);
rpcFreeCont
(
rpcMsg
->
pCont
);
return
;
}
...
...
@@ -394,11 +381,10 @@ void tscProcessMsgFromServer(SRpcMsg *rpcMsg, SRpcEpSet *pEpSet) {
(
*
pSql
->
fp
)(
pSql
->
param
,
pSql
,
rpcMsg
->
code
);
}
void
**
p1
=
p
;
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p1
,
false
);
taosReleaseRef
(
tscObjRef
,
pSql
->
self
);
if
(
shouldFree
)
{
// in case of table-meta/vgrouplist query, automatically free it
taos
CacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
true
);
taos
RemoveRef
(
tscObjRef
,
pSql
->
self
);
tscDebug
(
"%p sqlObj is automatically freed"
,
pSql
);
}
...
...
@@ -1829,14 +1815,14 @@ int tscProcessMultiMeterMetaRsp(SSqlObj *pSql) {
// pMeta->index = 0;
// (void)taosCachePut(tscMetaCache, pMeta->tableId, (char *)pMeta, size, tsTableMetaKeepTimer);
// }
}
}
pSql->res.code = TSDB_CODE_SUCCESS;
pSql->res.numOfTotal = i;
tscDebug("%p load multi-metermeta resp from complete num:%d", pSql, pSql->res.numOfTotal);
pSql->res.code = TSDB_CODE_SUCCESS;
pSql->res.numOfTotal = i;
tscDebug("%p load multi-metermeta resp from complete num:%d", pSql, pSql->res.numOfTotal);
#endif
return
TSDB_CODE_SUCCESS
;
return
TSDB_CODE_SUCCESS
;
}
int
tscProcessSTableVgroupRsp
(
SSqlObj
*
pSql
)
{
...
...
@@ -1962,7 +1948,7 @@ int tscProcessShowRsp(SSqlObj *pSql) {
// TODO multithread problem
static
void
createHBObj
(
STscObj
*
pObj
)
{
if
(
pObj
->
pHb
!=
NULL
)
{
if
(
pObj
->
hbrid
!=
0
)
{
return
;
}
...
...
@@ -1992,7 +1978,7 @@ static void createHBObj(STscObj* pObj) {
registerSqlObj
(
pSql
);
tscDebug
(
"%p HB is allocated, pObj:%p"
,
pSql
,
pObj
);
pObj
->
pHb
=
pSql
;
pObj
->
hbrid
=
pSql
->
self
;
}
int
tscProcessConnectRsp
(
SSqlObj
*
pSql
)
{
...
...
src/client/src/tscSql.c
浏览文件 @
b00fb8c1
...
...
@@ -277,8 +277,8 @@ void taos_close(TAOS *taos) {
pObj
->
signature
=
NULL
;
taosTmrStopA
(
&
(
pObj
->
pTimer
));
SSqlObj
*
pHb
=
pObj
->
pHb
;
if
(
pHb
!=
NULL
&&
atomic_val_compare_exchange_ptr
(
&
pObj
->
pHb
,
pHb
,
0
)
==
pHb
)
{
SSqlObj
*
pHb
=
(
SSqlObj
*
)
taosAcquireRef
(
tscObjRef
,
pObj
->
hbrid
);
if
(
pHb
!=
NULL
)
{
if
(
pHb
->
rpcRid
>
0
)
{
// wait for rsp from dnode
rpcCancelRequest
(
pHb
->
rpcRid
);
pHb
->
rpcRid
=
-
1
;
...
...
@@ -286,6 +286,7 @@ void taos_close(TAOS *taos) {
tscDebug
(
"%p HB is freed"
,
pHb
);
taos_free_result
(
pHb
);
taosReleaseRef
(
tscObjRef
,
pHb
->
self
);
}
int32_t
ref
=
T_REF_DEC
(
pObj
);
...
...
@@ -645,8 +646,7 @@ void taos_free_result(TAOS_RES *res) {
bool
freeNow
=
tscKillQueryInDnode
(
pSql
);
if
(
freeNow
)
{
tscDebug
(
"%p free sqlObj in cache"
,
pSql
);
SSqlObj
**
p
=
pSql
->
self
;
taosCacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
true
);
taosReleaseRef
(
tscObjRef
,
pSql
->
self
);
}
}
...
...
@@ -738,15 +738,8 @@ static void tscKillSTableQuery(SSqlObj *pSql) {
if
(
pSub
==
NULL
)
{
continue
;
}
void
**
p
=
taosCacheAcquireByKey
(
tscObjCache
,
&
pSub
,
sizeof
(
TSDB_CACHE_PTR_TYPE
));
if
(
p
==
NULL
)
{
continue
;
}
SSqlObj
*
pSubObj
=
(
SSqlObj
*
)
(
*
p
);
assert
(
pSubObj
->
self
==
(
SSqlObj
**
)
p
);
//SSqlObj* pHb = (SSqlObj*)taosAcquireRef(tscObjRef, pObj->hbrid);
SSqlObj
*
pSubObj
=
pSub
;
pSubObj
->
res
.
code
=
TSDB_CODE_TSC_QUERY_CANCELLED
;
if
(
pSubObj
->
rpcRid
>
0
)
{
rpcCancelRequest
(
pSubObj
->
rpcRid
);
...
...
@@ -754,7 +747,7 @@ static void tscKillSTableQuery(SSqlObj *pSql) {
}
tscQueueAsyncRes
(
pSubObj
);
taos
CacheRelease
(
tscObjCache
,
(
void
**
)
&
p
,
false
);
taos
ReleaseRef
(
tscObjRef
,
pSubObj
->
self
);
}
tscDebug
(
"%p super table query cancelled"
,
pSql
);
...
...
src/client/src/tscSub.c
浏览文件 @
b00fb8c1
...
...
@@ -179,8 +179,9 @@ static SSub* tscCreateSubscription(STscObj* pObj, const char* topic, const char*
fail:
tscError
(
"tscCreateSubscription failed at line %d, reason: %s"
,
line
,
tstrerror
(
code
));
if
(
pSql
!=
NULL
)
{
if
(
pSql
->
self
!=
NULL
)
{
taos_free_result
(
pSql
);
if
(
pSql
->
self
!=
0
)
{
//taos_free_result(pSql);
taosReleaseRef
(
tscObjRef
,
pSql
->
self
);
}
else
{
tscFreeSqlObj
(
pSql
);
}
...
...
src/client/src/tscSubquery.c
浏览文件 @
b00fb8c1
...
...
@@ -2199,6 +2199,7 @@ int32_t tscHandleInsertRetry(SSqlObj* pSql) {
STableDataBlocks
*
pTableDataBlock
=
taosArrayGetP
(
pCmd
->
pDataBlocks
,
pSupporter
->
index
);
int32_t
code
=
tscCopyDataBlockToPayload
(
pSql
,
pTableDataBlock
);
pCmd
->
pDataBlocks
=
tscDestroyBlockArrayList
(
pCmd
->
pDataBlocks
);
if
((
pRes
->
code
=
code
)
!=
TSDB_CODE_SUCCESS
)
{
tscQueueAsyncRes
(
pSql
);
...
...
src/client/src/tscSystem.c
浏览文件 @
b00fb8c1
...
...
@@ -16,6 +16,7 @@
#include "os.h"
#include "taosmsg.h"
#include "tcache.h"
#include "tref.h"
#include "trpc.h"
#include "tsystem.h"
#include "ttimer.h"
...
...
@@ -31,7 +32,7 @@
// global, not configurable
SCacheObj
*
tscMetaCache
;
SCacheObj
*
tscObjCache
;
int
tscObjRef
=
-
1
;
void
*
tscTmr
;
void
*
tscQhandle
;
void
*
tscCheckDiskUsageTmr
;
...
...
@@ -143,7 +144,12 @@ void taos_init_imp(void) {
int64_t
refreshTime
=
10
;
// 10 seconds by default
if
(
tscMetaCache
==
NULL
)
{
tscMetaCache
=
taosCacheInit
(
TSDB_DATA_TYPE_BINARY
,
refreshTime
,
false
,
tscFreeTableMetaHelper
,
"tableMeta"
);
#ifndef SQLOBJ_USE_CACHE
tscObjRef
=
taosOpenRef
(
4096
,
tscFreeRegisteredSqlObj
);
#else
tscObjCache
=
taosCacheInit
(
TSDB_CACHE_PTR_KEY
,
refreshTime
/
2
,
false
,
tscFreeRegisteredSqlObj
,
"sqlObj"
);
#endif
}
tscRefId
=
taosOpenRef
(
200
,
tscCloseTscObj
);
...
...
@@ -166,9 +172,9 @@ void taos_cleanup(void) {
taosCacheCleanup
(
m
);
}
m
=
tscObjCache
;
if
(
m
!=
NULL
&&
atomic_val_compare_exchange_ptr
(
&
tscObjCache
,
m
,
0
)
==
m
)
{
taosCacheCleanup
(
m
);
int
refId
=
atomic_exchange_32
(
&
tscObjRef
,
-
1
)
;
if
(
refId
!=
-
1
)
{
taosCloseRef
(
refId
);
}
m
=
tscQhandle
;
...
...
src/client/src/tscUtil.c
浏览文件 @
b00fb8c1
...
...
@@ -364,18 +364,18 @@ static void tscFreeSubobj(SSqlObj* pSql) {
void
tscFreeRegisteredSqlObj
(
void
*
pSql
)
{
assert
(
pSql
!=
NULL
);
SSqlObj
*
*
p
=
(
SSqlObj
**
)
pSql
;
STscObj
*
pTscObj
=
(
*
p
)
->
pTscObj
;
SSqlObj
*
p
=
*
(
SSqlObj
**
)
pSql
;
STscObj
*
pTscObj
=
p
->
pTscObj
;
assert
(
(
*
p
)
->
self
!=
0
&&
(
*
p
)
->
self
==
(
p
)
);
tscFreeSqlObj
(
*
p
);
assert
(
p
->
self
!=
0
);
tscFreeSqlObj
(
p
);
int32_t
ref
=
T_REF_DEC
(
pTscObj
);
assert
(
ref
>=
0
);
tscDebug
(
"%p free sqlObj completed, tscObj:%p ref:%d"
,
*
p
,
pTscObj
,
ref
);
tscDebug
(
"%p free sqlObj completed, tscObj:%p ref:%d"
,
p
,
pTscObj
,
ref
);
if
(
ref
==
0
)
{
tscDebug
(
"%p all sqlObj freed, free tscObj:%p"
,
*
p
,
pTscObj
);
tscDebug
(
"%p all sqlObj freed, free tscObj:%p"
,
p
,
pTscObj
);
taosRemoveRef
(
tscRefId
,
pTscObj
->
rid
);
}
}
...
...
@@ -750,6 +750,10 @@ int32_t tscMergeTableDataBlocks(SSqlObj* pSql, SArray* pTableDataBlockList) {
dataBuf
->
size
+=
(
finalLen
+
sizeof
(
SSubmitBlk
));
assert
(
dataBuf
->
size
<=
dataBuf
->
nAllocSize
);
char
*
p
=
realloc
(
dataBuf
->
pData
,
dataBuf
->
size
);
if
(
p
!=
NULL
)
{
dataBuf
->
pData
=
p
;
}
// the length does not include the SSubmitBlk structure
pBlocks
->
dataLen
=
htonl
(
finalLen
);
...
...
@@ -1487,6 +1491,8 @@ void tscGetSrcColumnInfo(SSrcColumnInfo* pColInfo, SQueryInfo* pQueryInfo) {
}
}
#ifndef SQLOBJ_USE_CACHE
#else
void
tscSetFreeHeatBeat
(
STscObj
*
pObj
)
{
if
(
pObj
==
NULL
||
pObj
->
signature
!=
pObj
||
pObj
->
pHb
==
NULL
)
{
return
;
...
...
@@ -1499,6 +1505,7 @@ void tscSetFreeHeatBeat(STscObj* pObj) {
SQueryInfo
*
pQueryInfo
=
tscGetQueryInfoDetail
(
&
pHeatBeat
->
cmd
,
0
);
pQueryInfo
->
type
=
TSDB_QUERY_TYPE_FREE_RESOURCE
;
}
#endif
/*
* the following four kinds of SqlObj should not be freed
...
...
@@ -1518,7 +1525,7 @@ bool tscShouldBeFreed(SSqlObj* pSql) {
}
STscObj
*
pTscObj
=
pSql
->
pTscObj
;
if
(
pSql
->
pStream
!=
NULL
||
pTscObj
->
pHb
==
pSql
||
pSql
->
pSubscription
!=
NULL
)
{
if
(
pSql
->
pStream
!=
NULL
||
pTscObj
->
hbrid
==
pSql
->
self
||
pSql
->
pSubscription
!=
NULL
)
{
return
false
;
}
...
...
@@ -1809,13 +1816,14 @@ void tscResetForNextRetrieve(SSqlRes* pRes) {
}
void
registerSqlObj
(
SSqlObj
*
pSql
)
{
int32_t
DEFAULT_LIFE_TIME
=
2
*
600
*
1000
;
// 1200 sec
//
int32_t DEFAULT_LIFE_TIME = 2 * 600 * 1000; // 1200 sec
int32_t
ref
=
T_REF_INC
(
pSql
->
pTscObj
);
tscDebug
(
"%p add to tscObj:%p, ref:%d"
,
pSql
,
pSql
->
pTscObj
,
ref
);
TSDB_CACHE_PTR_TYPE
p
=
(
TSDB_CACHE_PTR_TYPE
)
pSql
;
pSql
->
self
=
taosCachePut
(
tscObjCache
,
&
p
,
sizeof
(
TSDB_CACHE_PTR_TYPE
),
&
p
,
sizeof
(
TSDB_CACHE_PTR_TYPE
),
DEFAULT_LIFE_TIME
);
//TSDB_CACHE_PTR_TYPE p = (TSDB_CACHE_PTR_TYPE) pSql;
//pSql->self = taosCachePut(tscObjCache, &p, sizeof(TSDB_CACHE_PTR_TYPE), &p, sizeof(TSDB_CACHE_PTR_TYPE), DEFAULT_LIFE_TIME);
pSql
->
self
=
taosAddRef
(
tscObjRef
,
pSql
);
}
SSqlObj
*
createSimpleSubObj
(
SSqlObj
*
pSql
,
void
(
*
fp
)(),
void
*
param
,
int32_t
cmd
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录