Browse code

dispatcher(k): basic framework for load dispatching

- a lightweight system to do a fair distrubution based on load
- no dependency on dialog, by using an internal table of active calls
with minimal info, to keep the module suitable for small devices and
have good performances
- not completed, requires xavp support for a compact info structure to
be carried in transaction for each failover step

Daniel-Constantin Mierla authored on 12/02/2010 13:03:24
Showing 5 changed files
... ...
@@ -70,6 +70,7 @@
70 70
 #include "../../lib/srdb1/db_res.h"
71 71
 #include "../../str.h"
72 72
 
73
+#include "ds_ht.h"
73 74
 #include "dispatch.h"
74 75
 
75 76
 #define DS_TABLE_VERSION	1
... ...
@@ -79,7 +80,7 @@
79 79
 
80 80
 static int _ds_table_version = DS_TABLE_VERSION;
81 81
 
82
-#define DS_DUID_SIZE	16
82
+static ds_ht_t *_dsht_load = NULL;
83 83
 
84 84
 typedef struct _ds_attrs
85 85
 {
... ...
@@ -93,6 +94,7 @@ typedef struct _ds_dest
93 93
 	str uri;
94 94
 	int flags;
95 95
 	int priority;
96
+	int dload;
96 97
 	ds_attrs_t attrs;
97 98
 	struct ip_addr ip_address; 	/*!< IP-Address of the entry */
98 99
 	unsigned short int port; 	/*!< Port of the request URI */
... ...
@@ -1134,6 +1136,49 @@ int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash)
1134 1134
 	return 0;
1135 1135
 }
1136 1136
 
1137
+int ds_get_leastloaded(ds_set_t *dset)
1138
+{
1139
+	int j;
1140
+	int k;
1141
+	int t;
1142
+
1143
+	k = 0;
1144
+	t = dset->dlist[k].dload;
1145
+	for(j=1; j<dset->nr; j++)
1146
+	{
1147
+		if(!((dset->dlist[j].flags & DS_INACTIVE_DST)
1148
+				|| (dset->dlist[j].flags & DS_PROBING_DST)))
1149
+		{
1150
+			if(dset->dlist[j].dload<t)
1151
+			{
1152
+				k = j;
1153
+				t = dset->dlist[k].dload;
1154
+			}
1155
+		}
1156
+	}
1157
+	return k;
1158
+}
1159
+
1160
+int ds_update_load(struct sip_msg *msg, ds_set_t *dset, int setid, int dst)
1161
+{
1162
+	if(dset->dlist[dst].attrs.duid[0]=='\0')
1163
+	{
1164
+		LM_ERR("dst unique id not set for %d (%.*s)\n", setid,
1165
+				msg->callid->body.len, msg->callid->body.s);
1166
+		return -1;
1167
+	}
1168
+
1169
+	if(ds_add_cell(_dsht_load, &msg->callid->body,
1170
+			dset->dlist[dst].attrs.duid, setid)<0)
1171
+	{
1172
+		LM_ERR("cannot add load to %d (%.*s)\n", setid,
1173
+				msg->callid->body.len, msg->callid->body.s);
1174
+		return -1;
1175
+	}
1176
+	dset->dlist[dst].dload++;
1177
+	return 0;
1178
+}
1179
+
1137 1180
 static inline int ds_get_index(int group, ds_set_t **index)
1138 1181
 {
1139 1182
 	ds_set_t *si = NULL;
... ...
@@ -1309,10 +1354,14 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
1309 1309
 		case 8: /* use always first entry */
1310 1310
 			hash = 0;
1311 1311
 		break;
1312
-		case 9: /* weight based load */
1312
+		case 9: /* weight based distribution */
1313 1313
 			hash = idx->wlist[idx->wlast];
1314 1314
 			idx->wlast = (idx->wlast+1) % 100;
1315 1315
 		break;
1316
+		case 10: /* load based distribution */
1317
+			hash = ds_get_leastloaded(idx);
1318
+			ds_update_load(msg, idx, set, hash);
1319
+		break;
1316 1320
 		default:
1317 1321
 			LM_WARN("algo %d not implemented - using first entry...\n", alg);
1318 1322
 			hash = 0;
... ...
@@ -1804,3 +1853,44 @@ void ds_check_timer(unsigned int ticks, void* param)
1804 1804
 		}
1805 1805
 	}
1806 1806
 }
1807
+
1808
+void ds_ht_timer(unsigned int ticks, void *param)
1809
+{
1810
+	ds_cell_t *it;
1811
+	ds_cell_t *it0;
1812
+	time_t now;
1813
+	int i;
1814
+
1815
+	if(_dsht_load==NULL)
1816
+		return;
1817
+
1818
+	now = time(NULL);
1819
+	
1820
+	for(i=0; i<_dsht_load->htsize; i++)
1821
+	{
1822
+		/* free entries */
1823
+		lock_get(&_dsht_load->entries[i].lock);
1824
+		it = _dsht_load->entries[i].first;
1825
+		while(it)
1826
+		{
1827
+			it0 = it->next;
1828
+			if(it->expire!=0 && it->expire<now)
1829
+			{
1830
+				/* expired */
1831
+				if(it->prev==NULL)
1832
+					_dsht_load->entries[i].first = it->next;
1833
+				else
1834
+					it->prev->next = it->next;
1835
+				if(it->next)
1836
+					it->next->prev = it->prev;
1837
+				_dsht_load->entries[i].esize--;
1838
+				/* execute ds unload callback */
1839
+				ds_cell_free(it);
1840
+			}
1841
+			it = it0;
1842
+		}
1843
+		lock_release(&_dsht_load->entries[i].lock);
1844
+	}
1845
+	return;
1846
+}
1847
+
... ...
@@ -106,5 +106,11 @@ int ds_is_from_list(struct sip_msg *_m, int group);
106 106
  */
107 107
 void ds_check_timer(unsigned int ticks, void* param);
108 108
 
109
+
110
+/*! \brief
111
+ * Timer for checking active calls load
112
+ */
113
+void ds_ht_timer(unsigned int ticks, void *param);
114
+
109 115
 #endif
110 116
 
... ...
@@ -63,6 +63,7 @@
63 63
 #include "../../mem/mem.h"
64 64
 #include "../../mod_fix.h"
65 65
 
66
+#include "ds_ht.h"
66 67
 #include "dispatch.h"
67 68
 
68 69
 MODULE_VERSION
... ...
@@ -100,6 +101,8 @@ str ds_ping_from   = {"sip:dispatcher@localhost", 24};
100 100
 static int ds_ping_interval = 0;
101 101
 int ds_probing_mode  = 0;
102 102
 int ds_append_branch = 1;
103
+int ds_hash_size = 0;
104
+int ds_hash_expire = 7200;
103 105
 
104 106
 /* tm */
105 107
 struct tm_binds tmb;
... ...
@@ -175,6 +178,7 @@ static param_export_t params[]={
175 175
 	{"ds_ping_interval",   INT_PARAM, &ds_ping_interval},
176 176
 	{"ds_probing_mode",    INT_PARAM, &ds_probing_mode},
177 177
 	{"ds_append_branch",   INT_PARAM, &ds_append_branch},
178
+	{"ds_hash_sixe",       INT_PARAM, &ds_hash_size},
178 179
 	{0,0,0}
179 180
 };
180 181
 
... ...
@@ -232,6 +236,12 @@ static int mod_init(void)
232 232
 	if(init_data()!= 0)
233 233
 		return -1;
234 234
 
235
+	if(ds_hash_size>0)
236
+	{
237
+		if(ds_ht_init(1<<ds_hash_size, ds_hash_expire)<0)
238
+			return -1;
239
+		register_timer(ds_ht_timer, NULL, 10);
240
+	}
235 241
 	if(ds_db_url.s)
236 242
 	{
237 243
 		ds_db_url.len     = strlen(ds_db_url.s);
238 244
new file mode 100644
... ...
@@ -0,0 +1,278 @@
0
+/**
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
4
+ *
5
+ * This file is part of kamailio, a free SIP server.
6
+ *
7
+ * openser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * openser is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
+ */
21
+
22
+#include <regex.h>
23
+
24
+#include "../../mem/shm_mem.h"
25
+#include "../../mem/mem.h"
26
+#include "../../dprint.h"
27
+#include "../../lib/kcore/hash_func.h"
28
+#include "../../ut.h"
29
+
30
+#include "ds_ht.h"
31
+
32
+
33
+#define ds_compute_hash(_s)        core_case_hash(_s,0,0)
34
+#define ds_get_entry(_h,_size)    (_h)&((_size)-1)
35
+
36
+
37
+ds_cell_t* ds_cell_new(str *cid, char *did, int dset, unsigned int cellid)
38
+{
39
+	ds_cell_t *cell;
40
+	unsigned int msize;
41
+
42
+	msize = sizeof(ds_cell_t) + (cid->len + 1)*sizeof(char);
43
+
44
+	cell = (ds_cell_t*)shm_malloc(msize);
45
+	if(cell==NULL)
46
+	{
47
+		LM_ERR("no more shm\n");
48
+		return NULL;
49
+	}
50
+
51
+	memset(cell, 0, msize);
52
+	cell->cellid = cellid;
53
+	cell->dset = dset;
54
+	cell->callid.len = cid->len;
55
+	cell->callid.s = (char*)cell + sizeof(ds_cell_t);
56
+	memcpy(cell->callid.s, cid->s, cid->len);
57
+	cell->callid.s[cid->len] = '\0';
58
+	strcpy(cell->duid, did);
59
+	return cell;
60
+}
61
+
62
+int ds_cell_free(ds_cell_t *cell)
63
+{
64
+	if(cell==NULL)
65
+		return -1;
66
+	shm_free(cell);
67
+	return 0;
68
+}
69
+
70
+
71
+
72
+ds_ht_t *ds_ht_init(unsigned int htsize, int expire)
73
+{
74
+	int i;
75
+	ds_ht_t *dsht = NULL;
76
+
77
+	dsht = (ds_ht_t*)shm_malloc(sizeof(ds_ht_t));
78
+	if(dsht==NULL)
79
+	{
80
+		LM_ERR("no more shm\n");
81
+		return NULL;
82
+	}
83
+	memset(dsht, 0, sizeof(ds_ht_t));
84
+	dsht->htsize = htsize;
85
+	dsht->htexpire = expire;
86
+
87
+	dsht->entries = (ds_entry_t*)shm_malloc(dsht->htsize*sizeof(ds_entry_t));
88
+	if(dsht->entries==NULL)
89
+	{
90
+		LM_ERR("no more shm.\n");
91
+		shm_free(dsht);
92
+		dsht = NULL;
93
+		return NULL;
94
+	}
95
+	memset(dsht->entries, 0, dsht->htsize*sizeof(ds_entry_t));
96
+
97
+	for(i=0; i<dsht->htsize; i++)
98
+	{
99
+		if(lock_init(&dsht->entries[i].lock)==0)
100
+		{
101
+			LM_ERR("cannot initalize lock[%d]\n", i);
102
+			i--;
103
+			while(i>=0)
104
+			{
105
+				lock_destroy(&dsht->entries[i].lock);
106
+				i--;
107
+			}
108
+			shm_free(dsht->entries);
109
+			shm_free(dsht);
110
+			dsht = NULL;
111
+			return NULL;
112
+		}
113
+	}
114
+
115
+	return dsht;
116
+}
117
+
118
+int ds_ht_destroy(ds_ht_t *dsht)
119
+{
120
+	int i;
121
+	ds_cell_t *it, *it0;
122
+
123
+	if(dsht==NULL)
124
+		return -1;
125
+
126
+	for(i=0; i<dsht->htsize; i++)
127
+	{
128
+		/* free entries */
129
+		it = dsht->entries[i].first;
130
+		while(it)
131
+		{
132
+			it0 = it;
133
+			it = it->next;
134
+			ds_cell_free(it0);
135
+		}
136
+		/* free locks */
137
+		lock_destroy(&dsht->entries[i].lock);
138
+	}
139
+	shm_free(dsht->entries);
140
+	shm_free(dsht);
141
+	dsht = NULL;
142
+	return 0;
143
+}
144
+
145
+
146
+int ds_add_cell(ds_ht_t *dsht, str *cid, char *duid, int dset)
147
+{
148
+	unsigned int idx;
149
+	unsigned int hid;
150
+	ds_cell_t *it, *prev, *cell;
151
+	time_t now;
152
+
153
+	if(dsht==NULL || dsht->entries==NULL)
154
+		return -1;
155
+
156
+	hid = ds_compute_hash(cid);
157
+	
158
+	idx = ds_get_entry(hid, dsht->htsize);
159
+
160
+	now = time(NULL);
161
+	prev = NULL;
162
+	lock_get(&dsht->entries[idx].lock);
163
+	it = dsht->entries[idx].first;
164
+	while(it!=NULL && it->cellid < hid)
165
+	{
166
+		prev = it;
167
+		it = it->next;
168
+	}
169
+	while(it!=NULL && it->cellid == hid)
170
+	{
171
+		if(cid->len==it->callid.len 
172
+				&& strncmp(cid->s, it->callid.s, cid->len)==0)
173
+		{
174
+			lock_release(&dsht->entries[idx].lock);
175
+			return -2;
176
+		}
177
+		prev = it;
178
+		it = it->next;
179
+	}
180
+	/* add */
181
+	cell = ds_cell_new(cid, duid, dset, hid);
182
+	if(cell == NULL)
183
+	{
184
+		LM_ERR("cannot create new cell.\n");
185
+		lock_release(&dsht->entries[idx].lock);
186
+		return -1;
187
+	}
188
+	cell->expire = now + dsht->htexpire;
189
+	if(prev==NULL)
190
+	{
191
+		if(dsht->entries[idx].first!=NULL)
192
+		{
193
+			cell->next = dsht->entries[idx].first;
194
+			dsht->entries[idx].first->prev = cell;
195
+		}
196
+		dsht->entries[idx].first = cell;
197
+	} else {
198
+		cell->next = prev->next;
199
+		cell->prev = prev;
200
+		if(prev->next)
201
+			prev->next->prev = cell;
202
+		prev->next = cell;
203
+	}
204
+	dsht->entries[idx].esize++;
205
+	lock_release(&dsht->entries[idx].lock);
206
+	return 0;
207
+}
208
+
209
+int ds_del_cell(ds_ht_t *dsht, str *cid)
210
+{
211
+	unsigned int idx;
212
+	unsigned int hid;
213
+	ds_cell_t *it;
214
+
215
+	if(dsht==NULL || dsht->entries==NULL)
216
+		return -1;
217
+
218
+	hid = ds_compute_hash(cid);
219
+	
220
+	idx = ds_get_entry(hid, dsht->htsize);
221
+
222
+	/* head test and return */
223
+	if(dsht->entries[idx].first==NULL)
224
+		return 0;
225
+	
226
+	lock_get(&dsht->entries[idx].lock);
227
+	it = dsht->entries[idx].first;
228
+	while(it!=NULL && it->cellid < hid)
229
+		it = it->next;
230
+	while(it!=NULL && it->cellid == hid)
231
+	{
232
+		if(cid->len==it->callid.len 
233
+				&& strncmp(cid->s, it->callid.s, cid->len)==0)
234
+		{
235
+			/* found */
236
+			if(it->prev==NULL)
237
+				dsht->entries[idx].first = it->next;
238
+			else
239
+				it->prev->next = it->next;
240
+			if(it->next)
241
+				it->next->prev = it->prev;
242
+			dsht->entries[idx].esize--;
243
+			lock_release(&dsht->entries[idx].lock);
244
+			ds_cell_free(it);
245
+			return 0;
246
+		}
247
+		it = it->next;
248
+	}
249
+	lock_release(&dsht->entries[idx].lock);
250
+	return 0;
251
+}
252
+
253
+int ds_ht_dbg(ds_ht_t *dsht)
254
+{
255
+	int i;
256
+	ds_cell_t *it;
257
+
258
+	for(i=0; i<dsht->htsize; i++)
259
+	{
260
+		lock_get(&dsht->entries[i].lock);
261
+		LM_ERR("htable[%d] -- <%d>\n", i, dsht->entries[i].esize);
262
+		it = dsht->entries[i].first;
263
+		while(it)
264
+		{
265
+			LM_ERR("\tcell: %.*s\n", it->callid.len, it->callid.s);
266
+			LM_ERR("\tduid: %s\n", it->duid);
267
+			LM_ERR("\thid: %u expire: %u\n", it->cellid,
268
+					(unsigned int)it->expire);
269
+			LM_ERR("\tdset:%d\n", it->dset);
270
+			it = it->next;
271
+		}
272
+		lock_release(&dsht->entries[i].lock);
273
+	}
274
+	return 0;
275
+}
276
+
277
+
0 278
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+/**
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
4
+ *
5
+ * This file is part of kamailio, a free SIP server.
6
+ *
7
+ * openser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * openser is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
+ */
21
+		       
22
+#ifndef _DS_HT_H_
23
+#define _DS_HT_H_
24
+
25
+#include <time.h>
26
+
27
+#include "../../str.h"
28
+#include "../../locking.h"
29
+
30
+#define DS_DUID_SIZE	16
31
+
32
+typedef struct _ds_cell
33
+{
34
+    unsigned int cellid;
35
+	str callid;
36
+	char duid[DS_DUID_SIZE];
37
+	int dset;
38
+	time_t  expire;
39
+    struct _ds_cell *prev;
40
+    struct _ds_cell *next;
41
+} ds_cell_t;
42
+
43
+typedef struct _ds_entry
44
+{
45
+	unsigned int esize;
46
+	ds_cell_t *first;
47
+	gen_lock_t lock;	
48
+} ds_entry_t;
49
+
50
+typedef struct _ds_ht
51
+{
52
+	unsigned int htexpire;
53
+	unsigned int htsize;
54
+	ds_entry_t *entries;
55
+	struct _ds_ht *next;
56
+} ds_ht_t;
57
+
58
+ds_ht_t *ds_ht_init(unsigned int htsize, int expire);
59
+int ds_ht_destroy(ds_ht_t *dsht);
60
+int ds_add_cell(ds_ht_t *dsht, str *cid, char *did, int dset);
61
+int ds_del_cell(ds_ht_t *dsht, str *cid);
62
+
63
+int ds_ht_dbg(ds_ht_t *dsht);
64
+int ds_cell_free(ds_cell_t *cell);
65
+
66
+#endif