2015-07-29 12 views
7

Tôi có một dự án là dự án API Web, dự án của tôi được nhiều người dùng truy cập (nghĩa là rất nhiều người dùng thực sự). Khi dự án của tôi được truy cập từ giao diện người dùng (trang web sử dụng HTML 5) và người dùng thực hiện việc cập nhật hoặc truy xuất dữ liệu, ứng dụng phụ trợ (API web) sẽ viết một tệp nhật ký (tệp .log nhưng nội dung là JSON). Vấn đề là, khi được nhiều người dùng truy cập, giao diện người dùng không phản hồi (luôn tải). Vấn đề là trong quá trình viết của tệp nhật ký (tệp nhật ký duy nhất đang được nhiều người dùng thực sự truy cập). Tôi nghe nói rằng bằng cách sử dụng một kỹ thuật đa luồng có thể giải quyết vấn đề, nhưng tôi không biết phương pháp nào. Vì vậy, có lẽ ai cũng có thể giúp tôi. Đây là mã của tôi (xin lỗi nếu lỗi đánh máy, tôi sử dụng điện thoại thông minh của tôi và phiên bản di động của stack overflow):Nhiều người dùng đang viết cùng một tệp

public static void JsonInputLogging<T>(T m, string methodName) 
{ 
    MemoryStream ms = new MemoryStream(); 
    DataContractJsonSerializer ser = new 
      DataContractJsonSerializer(typeof(T)); 
    ser.WriteObject(ms, m); 
    string jsonString = Encoding.UTF8.GetString(ms.ToArray()); 
    ms.Close(); 
    logging("MethodName: " + methodName + Environment.NewLine + jsonString.ToString()); 
} 


public static void logging (string message) 
{ 
    string pathLogFile = "D:\jsoninput.log"; 
    FileInfo jsonInputFile = new FileInfo(pathLogFile); 
    if (File.Exists(jsonInputFile.ToString())) 
    { 
     long fileLength = jsonInputFile.Length; 
     if (fileLength > 1000000) 
     { 
      File.Move(pathLogFile, pathLogFile.Replace(*some new path*); 
     } 
    } 
    File.AppendAllText(pathLogFile, *some text*); 
} 
+0

tốt, tôi không chắc chắn, nhưng dự án đã được triển khai (hơn 200.000 khách hàng) –

Trả lời

5

Bạn phải hiểu một số nội bộ ở đây trước tiên. Đối với mỗi người dùng [x], ASP.Net sẽ sử dụng một quy trình công nhân duy nhất. Một quy trình công nhân chứa nhiều luồng. Nếu bạn đang sử dụng nhiều phiên bản trên đám mây, nó thậm chí còn tồi tệ hơn bởi vì sau đó bạn cũng có nhiều trường hợp máy chủ (tôi cho rằng đây không phải là trường hợp).

Một vài vấn đề ở đây:

  • Bạn có nhiều người sử dụng và do đó nhiều chủ đề.
  • Nhiều chủ đề có thể bế tắc nhau bằng cách ghi các tệp.
  • Bạn có nhiều tên miền ứng dụng và do đó có nhiều quy trình.
  • Nhiều quá trình có thể khóa ra nhau

Mở và khóa file

File.Open có một vài lá cờ cho khóa. Về cơ bản, bạn có thể khóa các tệp độc quyền cho mỗi quy trình, đây là một ý tưởng hay trong trường hợp này. Cách tiếp cận hai bước với ExistsOpen sẽ không hữu ích, bởi vì ở giữa một quy trình công nhân khác có thể làm điều gì đó. Về cơ bản, ý tưởng là gọi Open với quyền truy cập không độc quyền và nếu không thành công, hãy thử lại với một tên tệp khác.

Điều này về cơ bản giải quyết được vấn đề với nhiều quy trình.

Viết từ nhiều luồng

truy cập File là đơn luồng. Thay vì viết nội dung của bạn vào một tệp, bạn có thể muốn sử dụng một chuỗi riêng biệt để thực hiện truy cập tệp và nhiều luồng cho biết điều cần viết.

Nếu bạn có nhiều yêu cầu đăng nhập hơn bạn có thể xử lý, bạn đang ở trong khu vực không đúng cách. Trong trường hợp đó, cách tốt nhất để xử lý nó để ghi nhật ký IMO là chỉ cần thả dữ liệu. Nói cách khác, làm cho trình ghi nhật ký phần nào mất mát để làm cho cuộc sống tốt hơn cho người dùng của bạn. Bạn có thể sử dụng hàng đợi cho điều đó.

Tôi thường sử dụng ConcurrentQueue cho chủ đề này và một chuỗi riêng biệt hoạt động với tất cả dữ liệu đã đăng nhập.

này về cơ bản là làm thế nào để làm điều này:

// Starts the worker thread that gets rid of the queue: 
internal void Start() 
{ 
    loggingWorker = new Thread(LogHandler) 
    { 
     Name = "Logging worker thread", 
     IsBackground = true, 
     Priority = ThreadPriority.BelowNormal 
    }; 
    loggingWorker.Start(); 
} 

Chúng ta cũng cần một cái gì đó để làm công việc thực tế và một số biến được chia sẻ:

private Thread loggingWorker = null; 
private int loggingWorkerState = 0; 
private ManualResetEventSlim waiter = new ManualResetEventSlim(); 
private ConcurrentQueue<Tuple<LogMessageHandler, string>> queue = 
    new ConcurrentQueue<Tuple<LogMessageHandler, string>>(); 

private void LogHandler(object o) 
{ 
    Interlocked.Exchange(ref loggingWorkerState, 1); 

    while (Interlocked.CompareExchange(ref loggingWorkerState, 1, 1) == 1) 
    { 
     waiter.Wait(TimeSpan.FromSeconds(10.0)); 
     waiter.Reset(); 

     Tuple<LogMessageHandler, string> item; 
     while (queue.TryDequeue(out item)) 
     { 
      writeToFile(item.Item1, item.Item2); 
     } 
    } 
} 

Về cơ bản mã này cho phép bạn làm việc đi tất cả các mục từ một chuỗi đơn sử dụng hàng đợi được chia sẻ qua các chuỗi. Lưu ý rằng ConcurrentQueue không sử dụng khóa cho TryDequeue, vì vậy, khách hàng sẽ không cảm thấy đau vì điều này.

Điều cuối cùng cần thiết là thêm nội dung vào hàng đợi. Đó là phần dễ dàng:

public void Add(LogMessageHandler l, string msg) 
{ 
    if (queue.Count < MaxLogQueueSize) 
    { 
     queue.Enqueue(new Tuple<LogMessageHandler, string>(l, msg)); 
     waiter.Set(); 
    } 
} 

Mã này sẽ được gọi từ nhiều chuỗi. Nó không đúng 100% vì CountEnqueue không nhất thiết phải được gọi theo cách nhất quán - nhưng với mục đích và mục đích của chúng tôi thì nó đủ tốt. Nó cũng không khóa trong các Enqueuewaiter sẽ đảm bảo rằng các công cụ được loại bỏ bởi các chủ đề khác.

Kết hợp tất cả điều này theo mẫu đơn, thêm một số logic vào nó và vấn đề của bạn sẽ được giải quyết.

+0

PS cũng có một vấn đề tương tranh trong 'waiter.Wait' /' waiter.Set', đó là lý do tại sao 'Timespan' được chỉ định - một lần nữa, nó sẽ là tốt cho trường hợp đăng nhập. – atlaste

+0

ok sir, tôi sẽ cố gắng gợi ý của bạn, hy vọng nó có thể giải quyết vấn đề của tôi, chúc tôi may mắn –

+0

@wulung_try Tôi đang sử dụng mã tương tự cho điều này (với một số logic hơn) để đăng xuất từ ​​trình thu thập dữ liệu web của tôi; vì vậy bạn nên được tốt :-) – atlaste

1

Đó có thể là vấn đề, vì mọi yêu cầu của khách hàng xử lý bởi chủ đề mới theo mặc định anyway. Bạn cần một số đối tượng "root" được biết đến trong dự án (không nghĩ rằng bạn có thể đạt được điều này trong lớp tĩnh), vì vậy bạn có thể khóa nó trước khi bạn truy cập tệp nhật ký. Tuy nhiên, lưu ý rằng về cơ bản nó sẽ sắp xếp các yêu cầu, và có lẽ sẽ có ảnh hưởng rất xấu đến hiệu năng.

1

Không đa luồng không giải quyết được sự cố của bạn. Làm thế nào là nhiều chủ đề phải ghi vào cùng một tập tin cùng một lúc? Bạn cần phải quan tâm đến dữ liệu nhất quán và tôi không nghĩ đó là vấn đề thực tế ở đây.

Điều bạn tìm kiếm là không đồng bộ lập trình. Lý do GUI của bạn trở nên không phản hồi là nó đợi các nhiệm vụ hoàn thành. Nếu bạn biết, logger là nút cổ chai của bạn sau đó sử dụng async để lợi thế của bạn. Cháy phương pháp đăng nhập và quên kết quả, chỉ cần viết tệp.

Thực ra tôi không thực sự nghĩ rằng trình ghi nhật ký của bạn là vấn đề. Bạn có chắc chắn không có logic khác chặn bạn?

+0

không có logic khác, đôi khi nó thực sự đáp ứng, đôi khi phải mất một thời gian dài, và cuối cùng nó luôn tải ...: ( –

Các vấn đề liên quan