需要帮助来确定什么是跟踪系统的最佳解决方案(.NET)

时间:2009-04-03 00:45:06

标签: c# .net asp.net multithreading msmq

这是我在这里的第一个问题,我的名字是安娜!

我的问题: 我的客户有几个个人设备(带有gps的黑盒子)来定位人/车...大约有12000人/车使用该设备......它将他们的位置发送到指定的IP /端口......我无能为力那边......

我的工作? Devolope是一个监听器,用于捕获设备发送的所有数据,并使用.NET加载数据库...

我的想法: 使用线程的Window Service(也许是ThreadPool?)。因此,该服务将捕获所有传入的消息,创建一个线程并放入DB ...

这是问题的最佳解决方案吗? 我在这里读到有关Message Queue(MSMQ)的信息......您认为我应该使用它吗?

安娜

2 个答案:

答案 0 :(得分:3)

位置/时间的数量,以及位置信息的传输方式(TCP,UDP等)将有助于确定最佳方法。

有些问题:

  • 12000设备发送消息的频率是多少?
  • 地点如何传输? UDP? TCP?
  • 您有什么可靠性要求?

从它的声音来看,拥有一个可以捕获请求的服务,只是在内部维护一个队列以保存到数据库中的事情应该可以正常工作。我不相信MSMQ会对你有用,除非你可以改变传输方面,即便如此,它可能也可能没有必要。


编辑:鉴于下面的评论,我建议你有一个TCP侦听器将请求传递给线程池来处理。

我会看一下this tutorial关于使用线程池设置TCP侦听服务器的问题。我看到的最大的潜在问题是请求的数量 - 从你所说的,你将有大约400个请求/秒。如果没有一个相当不错的系统,这将是一个挑战。线程池可能比尝试执行自己的线程更好,因为您需要避免不断创建新线程的开销。你肯定希望在主处理循环中有很少的延迟(比如Sleep(0),或根本没有睡眠),因为你平均每2.5 ms会有一个请求。单次睡眠倾向于在最短14-15 ms时间切片,因此您可能不希望在循环中进行任何睡眠。

但是,我会说,你可能会发现这种方法效果不好,因为连接的原始数量可能会有问题。如果有任何方法可以转换为正在发送的UDP数据包,它可能会提高吞吐量(以牺牲一些可靠性为代价)。

答案 1 :(得分:2)

以下代码尚未经过测试或仅被视为指导。 它使用线程池来处理所有事情。我已将所有内容放在同一个文件中仅用于示例。你应该将所有内容分解成几个文件。

重要的是不要让所有客户端直接保存到DB,因为这会使线程池匮乏。尝试使用“MaxThreads”常量来获取与db / server一起使用的值。

另外请记住,我根本不处理任何例外情况。您需要在BeginRead,EndRead和TcpListener方法上处理SocketException实例。

我尝试使用最少数量的线程同步锁,代码应该非常高效。瓶颈很可能是数据库。

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace FastListener
{
    /// <summary>
    /// Example position class, replace with a real definition
    /// </summary>
    public class Position
    {
        public int X { get; set; }
        public int Y { get; set; }
    }

    /// <summary>
    /// Needed to be able to pass socket/buffer info
    /// between asynchronous requests.
    /// </summary>
    public struct ClientContext
    {
        public Socket socket;
        public byte[] buffer;
    }

    class Program
    {
        /// <summary>
        /// Positions received from mobile clients but not yet saved
        /// into the database.
        /// </summary>
        private readonly Queue<Position> _positions = new Queue<Position>();

        /// <summary>
        /// Number of threads currently saving stuff to the database.
        /// </summary>
        private int _poolThreads;

        /// <summary>
        /// Maximum number of threads that can save info to the database.
        /// </summary>
        private const int MaxThreads = 10;

        static void Main(string[] args)
        {
            new Program().Start();
        }

        private void Start()
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 1343);
            listener.Start(50);
            listener.BeginAcceptSocket(OnAccept, listener);
        }

        // Listener got a new connection
        private void OnAccept(IAsyncResult ar)
        {
            TcpListener listener = (TcpListener) ar.AsyncState;

            // It's very important to start listening ASAP
            // since you'll have a lot of incoming connections.
            listener.BeginAcceptSocket(OnAccept, listener);

            // I recommend that you create a buffer pool to improve performance
            byte[] buffer = new byte[400];

            // Now accept the socket.
            Socket socket = listener.EndAcceptSocket(ar);
            StartRead(new ClientContext {buffer = buffer, socket = socket});
        }

        private void StartRead(ClientContext context)
        {
            // start reading from the client.
            context.socket.BeginReceive(context.buffer, 0, 400, SocketFlags.None, OnReceive, context);
        }


        // Stuff from a client.
        private void OnReceive(IAsyncResult ar)
        {
            ClientContext context = (ClientContext) ar.AsyncState;

            int bytesRead = context.socket.EndReceive(ar);
            if (bytesRead == 0)
            {
                // Put the buffer back in the pool
                context.socket.Close();
                return;
            }

            // convert bytes to position.
            // i'll just fake that here.
            Position pos = new Position();

            // Either handle the request directly
            if (_poolThreads < MaxThreads)
                ThreadPool.QueueUserWorkItem(SaveToDatabase, pos);
            else
            {
                // Or enqueue it to let a already active
                // thread handle it when done with the previous position
                lock (_positions)
                    _positions.Enqueue(pos);
            }

            // Don't forget to read from the client again
            StartRead(context); 
        }

        // will save stuff to the database.
        private void SaveToDatabase(object state)
        {
            // Could use Interlocked.Increment, but not really vital if 
            // one more more extra threads are saving to the db.
            ++_poolThreads; 

            Position position = (Position) state;
            while (true)
            {
                // IMPLEMENT DB SAVE LOGIC HERE.


                // check if another position is in the queue.
                lock (_positions)
                {
                    if (_positions.Count > 0)
                        position = _positions.Dequeue();
                    else
                        break; // jump out of the loop
                }
            }

            --_poolThreads;
        }
    }
}