天天看點

Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼

伺服器在調用<code>listen</code>和<code>accept</code>後,就會阻塞在<code>accept</code>函數上,<code>accpet</code>函數傳回後循環調用<code>accept</code>函數等待客戶的<code>tcp</code>連接配接。

我們知道伺服器段<code>listen</code>套接字能處理的連接配接數與監聽隊列的大小有關,如果這時候又大量的使用者并發發起<code>connec</code>連接配接,那麼在<code>listen</code>有隊列上限(最大可接受tcp的連接配接數)的情況下,有多少個<code>connect</code>會成功了。

試驗證明,當連接配接數遠遠高于<code>listen</code>的可連接配接數上限時,用戶端的大部分<code>tcp</code>請求會被抛棄,隻有當<code>listen</code>監聽隊列空閑或者放棄某個連接配接時,才可以接收新的連接配接

那麼我們當并發的連接配接數較大時,伺服器應該如何來避免這種情況出現?

用戶端如何模拟高并發呢?

用戶端運作初期完成所設定的一定量的<code>socket</code>建立和相應的處理線程的建立,然後使用條件變量來完成線程同步,直到最後一個線程建立完成,才向所有線程發出廣播通知,讓所有線程并發調用<code>connect</code>(這樣相當于所有的用戶端在同一時間請求連接配接,即高并發),連接配接成功則關閉連接配接,失敗則傳回。

Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼

關于 linux系統中程序所能建立的線程數目?

每個程序需要自己獨立的棧空間,linux預設的線程棧大小是10mb。在32位的機子上一個程序需要4g的記憶體空間,去掉自己的棧空間全局程式段空間,一般隻有3g記憶體可以用,建立線程時就需要從這3g的空間中配置設定10m出來,是以最多可以配置設定300多個線程。

當然這裡還可以使用多個程序,每個程序300個線程的方式來進一步擴大并發量。

當然64位系統中,此數值會不同,甚至會大很多

linux最大線程數限制及目前線程數查詢: 總結系統限制有: <code>cat /proc/sys/kernel/pid_max</code> #查系統支援的最大線程數,一般會很大,相當于理論值 <code>cat /proc/sys/kernel/threads-max</code> <code>max_user_process</code>(<code>ulimit -u</code>) #系統限制某使用者下最多可以運作多少程序或線程 <code>/proc/sys/vm/max_map_count</code> 硬體記憶體大小

我們在用戶端程式中,為我們模拟每個用戶端建立一個線程

1

2

3

4

5

6

7

8

9

10

11

然後在每個用戶端線程中,建立并初始化用戶端的套接字描述符。

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待”條件變量的條件成立”而挂起;另一個線程使”條件成立”(給出條件成立信号)。為了防止競争,條件變量的使用總是和一個互斥鎖結合在一起。

當然這裡有個關鍵問題,就是如何保證模拟出來所有的用戶端同步在連接配接伺服器之前

在這裡就使用條件變量進行同步。

條件變量初始化
Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼
在connect之前設定條件變量,
Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼
注意:阻塞傳回後立即解鎖,防止互斥量加鎖帶來的阻塞 <code>pthread_mutex_unlock(&amp;mut);</code>

當條件滿足時,喚醒阻塞在條件變量上的線程:

綜上,用戶端模拟并發過程中沒有存在不同步的情況導緻上述性能問題。(注意,在廣播的時候,會出現廣播丢失的情況,是以需要多次執行廣播操作才會使得所有線程執行任務,是以某種程度上這裡并不能模拟完完全全的并發)

[注意] 用戶端和伺服器之間的連接配接是在同一台機器上,使用socket方式通信的話會經過127.0.0.1的回環線路,不會有網卡等硬體資源的通路性能消耗,是以不存在網絡通信時延等問題。

性能問題主要發生在伺服器,可能是以下幾部分造成:

伺服器的監聽隊列<code>listen(listenfd,xxx)</code>

參數2指定隊列内所能容納的元素的最大值,

當來不及從隊列中移除元素時(調用accept移除或者tcp自動放棄)就會造成隊列滿而使得一些請求丢失。

解決辦法

增大隊列容量是一種辦法,但是注意等待隊列太會帶來效率的性能缺陷,而且<code>listen</code>函數對最大隊列容量有一個上限,大小為<code>somaxconn</code>,當然必要時刻我們可以修改這個常量的大小。

直接修改<code>listen</code>及相關函數的實作(比較麻煩,不建議),可以将<code>listen</code>所維護的隊列修改為<code>linklist</code>,支援隊列的動态增長。

<code>accept</code>導緻阻塞過長時間,使得隊列無法及時清空已經完成3次連接配接的<code>socket</code>

也就是任意兩個<code>accept</code>之間的時間間隔是關鍵因素,如下面代碼所示(一般來說,可以通過<code>listen</code>之後在循環調用<code>accept</code>來将已完成3次握手的連接配接從listen所維護的隊列中移除。

解決辦法:

* 兩個accept之間盡量不要又多餘的操作,使得accept傳回後可以立刻執行下一個accept。經過試驗,該方法可以較好的提高性能,減少connect的丢失數。

本質上這是一種“生産者-消費者”的模式,listen維護“已連接配接”和“待連接配接”的隊列,當客戶發出連接配接請求并最終連接配接成功時,在“已連接配接”隊列中會生産一個“product”,然後這時候希望“消費者”也就是accept函數可以快速的從隊列中消費這個“product”,這樣就不會因為隊列滿而導緻無法繼續生産(也就是客戶的connect會失效,導緻上面隊列長度10,300個并發connect帶來的67個存活的情況),但是在本例情況下,我們無法控制生産者的瘋狂生産行為,因為連接配接是客戶發起的,這是不可預知的,是以我們如果想不修改listen函數來提高性能的話,那麼就隻能讓消費者更加快的把産品消耗掉,使得listen隊列可以容納更多的新生産的産品,而第一種加快消費者消耗産品的方法就是a,第二種加快消費者消耗産品的方法是我們可以增加多幾個消費者來幫忙消耗,但是這幾個消費者間也要好好協調。第三種方法是讓消費者把産品先移走為listen的隊列騰出空間,再自行處理産品,如d所示。

使用多線程政策,每個線程獨立調用accept。(花了一個上午的時候正glib的線程池,一直用不了,其他都正常,就是線程不啟動,不知道會不會是bug)

修改listen和accept的實作方式,讓listen所維護的隊列可以智能的判斷擁擠情況,進而對accept的調用做出排程,在隊列繁忙時,使用多線程的方式讓多個accept來移除隊列中的元素,在隊列空閑時,可以适當的調整accept的處理線程數,這也是一種線程池的實作。

修改accept的實作方式,在accept中實作一個“消費緩沖區”,為的是及時将listen中的隊列元素移動到該緩沖區中,再由其他處理線程或者程序來對緩沖區中的元素進行處理,這個方法盡量讓listen隊列中已連接配接的socket可以被移除。這個方法比較上述方法來說是比較好的一種,但是還是需要修改已有的代碼。

伺服器短開辟一個線程池,線程完成與原來單線程的伺服器執行相同的操作,accept接受用戶端的請求,并且開辟線程進行響應進行

伺服器線程池
線程池中的線程完成同原來伺服器主線程完全相同的操作

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼

轉載:http://blog.csdn.net/gatieme/article/details/50828863