最近使用到了Net-Snmp来做一个设备信息查询的manager,因为在使用的时候查询的设备以及需要的oid比较多,使用同步的方式一个一个查询会使查询速度慢,所以在这里有两种解决方式。
- 多线程
因为刚开始使用Net-snmp不是很熟悉,所以立马想到的就是使用多个线程提高并发。
关于多线程的API,可以在Net-SNMP的rREADME文档看到如下内容:
Traditional Single Comment
=========== ============== =======
snmp_sess_init snmp_sess_init Call before either open
snmp_open snmp_sess_open Single not on Sessions list
snmp_sess_session Exposes snmp_session pointer
snmp_send snmp_sess_send Send one APDU
snmp_async_send snmp_sess_async_send Send one APDU with callback
snmp_select_info snmp_sess_select_info Which session(s) have input
snmp_read snmp_sess_read Read APDUs
snmp_timeout snmp_sess_timeout Check for timeout
snmp_close snmp_sess_close Single not on Sessions list
snmp_synch_response snmp_sess_synch_response Send/receive one APDU
snmp_error snmp_sess_error Get library,system errno
也就是说要想使用多线程的方式去使用NetSNMP的话需要使用SingleAPI,按照文档所说的,使用多线程查询的代码大致如下:
//多线程使用NET-SNMP
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
using namespace std;
const short HOST_COUNT = 3; //主机数
const short OID_COUNT = 2; //查询的oid数
const char* host_array[3]={
"192.168.6.1",
"192.168.6.2",
"192.168.6.3"
};
const char* oid_array[2]={
".1.3.6.1.2.1.1.1.0", //系统基础信息
".1.3.6.1.2.1.1.3.0" //系统时间
};
typedef struct{
oid o[MAX_OID_LEN];
size_t l;
}cc_oid;
typedef struct{
char* ip;
void *pSession;
vector<cc_oid> listOid;
//int currentOid;
}Host;
vector<Host> g_vecHosts;
void init_hosts()
{
for(int i=0; i<HOST_COUNT; i++)
{
Host h;
h.ip = const_cast<char*>(host_array[i]);
h.listOid.clear();
for(int j=0; j<OID_COUNT; j++)
{
cc_oid o;
o.l = MAX_OID_LEN;
if(!read_objid(oid_array[j], o.o, &o.l))
{
cout<<"parse oid failed"<<endl;
return;
}
h.listOid.push_back(o);
}
g_vecHosts.push_back(h);
}
}
void init_sessions()
{
for(int i=0; i<g_vecHosts.size(); i++)
{
Host *h = &g_vecHosts[i];
netsnmp_session session;;
snmp_sess_init(&session);
session.peername = const_cast<char*>(host_array[i]);
session.retries = 0;
session.timeout = 1000000;
session.remote_port = 161;
session.version = SNMP_VERSION_2c;
session.community = (u_char*)strdup("public");
session.community_len = strlen("public");
h->pSession = snmp_sess_open(&session);
if(!h->pSession)
{
cout<<"snmp open failed :"<<host_array[i]<<endl;
}
}
}
void get_result(struct snmp_pdu* pdu);
void query(uint index)
{
netsnmp_pdu* response;
if(index >= g_vecHosts.size())
{
return;
}
if(!g_vecHosts[index].pSession)
{
cout<<"psession null"<<endl;
return;
}
while(true)
{
response = nullptr;
struct snmp_pdu* pdu = snmp_pdu_create(SNMP_MSG_GET);
for(auto& o : g_vecHosts[index].listOid)
{
snmp_add_null_var(pdu, o.o, o.l);
}
int ret = snmp_sess_synch_response(g_vecHosts[index].pSession, pdu, &response);
if(ret == STAT_SUCCESS && nullptr != response)
{
//get result
cout<<"get result"<<endl;
get_result(response);
}
else
{
}
sleep(1);
}
}
void get_result(struct snmp_pdu* pdu)
{
struct variable_list *vp = pdu->variables;
if(vp && pdu->errstat==SNMP_ERR_NOERROR)
{
char buf[1024];
while(vp)
{
snprint_variable(buf, sizeof(buf), vp->name, vp->name_length, vp);
fprintf(stdout, " %s\n", buf);
vp=vp->next_variable;
}
}
}
int main()
{
SOCK_STARTUP;
init_snmp("snmpget");
init_hosts();
init_sessions();
vector<thread> queryThreads;
for(int i=0; i<g_vecHosts.size(); i++)
{
queryThreads.push_back(thread(query, i));
}
for(auto &t : queryThreads)
{
if(t.joinable())
t.join();
}
SOCK_CLEANUP;
}
运行结果:
但是在实际运行过程中总是出现崩溃的现象,查看core文件发现是在snmp_sess_open()函数调用的时候崩溃,但是这个问题只是在运行了一段时间的时候出现,在可以在网上查到对应的解决,连接如下:
https://bugzilla.redhat.com/show_bug.cgi?id=1366282
大致意思是,这是之前Net-SNMP的一个bug,后续版本有解决,需要打补丁。如果有需要的可以找到对应的patch尝试。
- 异步
考虑到多线程的不确定性,准备尝试使用异步方式完成oid’的查询,而且官方的demo就是使用异步来查询的,貌似Net-Snmp早期的版本也是不支持多线程的。为此封装了部分异步Net-Snmp的接口,如果有需要的可以查看,代码如下: