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