本系列文章詳細介紹使用 .net core 和 WPF 開發 升訊威線上客服與營銷系統 的過程。本產品已經成熟穩定並投入商用。
線上演示環境:https://kf.shengxunwei.com 注意:演示環境僅供演示交流與評估,不保證 7x24 小時可用。
對於線上客服與營銷系統,客服端指的是後臺提供服務的客服或營銷人員,他們使用客服程式在後臺觀察網站的被訪情況,開展營銷活動或提供客戶服務。在本篇文章中,我將詳細介紹如何在 .net core 環境下使用 TCP 通訊技術實現穩定高效與安全的客服端程式。
這裡存在幾個技術難點需要注意:
- 需要使客服端程式具備 24 小時不間斷執行的能力,在處理網路通訊時,必須100%的穩定。
- 必須具備應對網路波動的能力,不能網路稍有波動就斷線。即使出現了短暫的網路中斷,客服程式也不能做掉線處理,而是要具備保持和自動重連的能力。
- 要考慮安全性問題,服務端的埠監聽,要能識別正常客服端連線,還是來自攻擊者的連線。
訪客端實現的效果:
訪客端在手機上的效果:
後臺客服的實現效果:
執行緒安全集合
System.Collections.Concurrent 名稱空間,其中包含多個執行緒安全且可縮放的集合類。 多個執行緒可以安全高效地從這些集合新增或刪除項,而無需在使用者程式碼中進行其他同步。 編寫新程式碼時,只要將多個執行緒同時寫入到集合時,就使用併發集合類。
細粒度鎖定和無鎖機制
某些併發集合型別使用輕量同步機制,如 SpinLock、SpinWait、SemaphoreSlim 和 CountdownEvent。 這些同步型別通常在將執行緒真正置於等待狀態之前,會在短時間內使用 忙旋轉。 預計等待時間非常短時,旋轉比等待所消耗的計算資源少得多,因為後者涉及資源消耗量大的核心轉換。 對於使用旋轉的集合類,這種效率意味著多個執行緒能夠以非常快的速率新增和刪除項。
BlockingCollection
BlockingCollection 是一個執行緒安全集合類,可提供以下內容:
- 生成者/使用者模式的實現; BlockingCollection 是介面的包裝 IProducerConsumerCollection 。
- 利用和方法併發新增和移除多個執行緒中的項 Add Take 。
- Add Take 當集合已滿或為空時阻止和操作的繫結集合。
- Add Take 使用 CancellationToken 或方法中的物件取消或操作 TryAdd TryTake 。
下面的示例演示如何使用:
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class BlockingCollectionDemo
{
static async Task Main()
{
await AddTakeDemo.BC_AddTakeCompleteAdding();
TryTakeDemo.BC_TryTake();
FromToAnyDemo.BC_FromToAny();
await ConsumingEnumerableDemo.BC_GetConsumingEnumerable();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
class AddTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.Take()
// BlockingCollection<T>.CompleteAdding()
public static async Task BC_AddTakeCompleteAdding()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Spin up a Task to populate the BlockingCollection
Task t1 = Task.Run(() =>
{
bc.Add(1);
bc.Add(2);
bc.Add(3);
bc.CompleteAdding();
});
// Spin up a Task to consume the BlockingCollection
Task t2 = Task.Run(() =>
{
try
{
// Consume consume the BlockingCollection
while (true) Console.WriteLine(bc.Take());
}
catch (InvalidOperationException)
{
// An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!");
}
});
await Task.WhenAll(t1, t2);
}
}
}
class TryTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.CompleteAdding()
// BlockingCollection<T>.TryTake()
// BlockingCollection<T>.IsCompleted
public static void BC_TryTake()
{
// Construct and fill our BlockingCollection
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
int NUMITEMS = 10000;
for (int i = 0; i < NUMITEMS; i++) bc.Add(i);
bc.CompleteAdding();
int outerSum = 0;
// Delegate for consuming the BlockingCollection and adding up all items
Action action = () =>
{
int localItem;
int localSum = 0;
while (bc.TryTake(out localItem)) localSum += localItem;
Interlocked.Add(ref outerSum, localSum);
};
// Launch three parallel actions to consume the BlockingCollection
Parallel.Invoke(action, action, action);
Console.WriteLine("Sum[0..{0}) = {1}, should be {2}", NUMITEMS, outerSum, ((NUMITEMS * (NUMITEMS - 1)) / 2));
Console.WriteLine("bc.IsCompleted = {0} (should be true)", bc.IsCompleted);
}
}
}
class FromToAnyDemo
{
// Demonstrates:
// Bounded BlockingCollection<T>
// BlockingCollection<T>.TryAddToAny()
// BlockingCollection<T>.TryTakeFromAny()
public static void BC_FromToAny()
{
BlockingCollection<int>[] bcs = new BlockingCollection<int>[2];
bcs[0] = new BlockingCollection<int>(5); // collection bounded to 5 items
bcs[1] = new BlockingCollection<int>(5); // collection bounded to 5 items
// Should be able to add 10 items w/o blocking
int numFailures = 0;
for (int i = 0; i < 10; i++)
{
if (BlockingCollection<int>.TryAddToAny(bcs, i) == -1) numFailures++;
}
Console.WriteLine("TryAddToAny: {0} failures (should be 0)", numFailures);
// Should be able to retrieve 10 items
int numItems = 0;
int item;
while (BlockingCollection<int>.TryTakeFromAny(bcs, out item) != -1) numItems++;
Console.WriteLine("TryTakeFromAny: retrieved {0} items (should be 10)", numItems);
}
}
class ConsumingEnumerableDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.CompleteAdding()
// BlockingCollection<T>.GetConsumingEnumerable()
public static async Task BC_GetConsumingEnumerable()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Kick off a producer task
await Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
bc.Add(i);
await Task.Delay(100); // sleep 100 ms between adds
}
// Need to do this to keep foreach below from hanging
bc.CompleteAdding();
});
// Now consume the blocking collection with foreach.
// Use bc.GetConsumingEnumerable() instead of just bc because the
// former will block waiting for completion and the latter will
// simply take a snapshot of the current state of the underlying collection.
foreach (var item in bc.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
}
}
}
本文對使用執行緒安全的 BlockingCollection 實現高效能的資料處理進行了簡要的介紹,在接下來的文章中,我將具體解構服務端程式的結構和設計、客服端程式的結構和設計,敬請關注。
線上演示環境:https://kf.shengxunwei.com 注意:演示環境僅供演示交流與評估,不保證 7x24 小時可用。
聯絡QQ: 279060597
聯絡E-mail:C5118@outlook.com