Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 678a6a2b

Přidáno uživatelem Jakub Šilhavý před asi 2 roky(ů)

re #9570 Commented ApiClient.cs

Zobrazit rozdíly:

ld_client/LDClient/network/ApiClient.cs
8 8

  
9 9
namespace LDClient.network {
10 10
    
11
    /// <summary>
12
    /// This class implements IApiClient which is an interface
13
    /// defining all the functionality required from an API client.
14
    /// </summary>
11 15
    public sealed class ApiClient : IApiClient {
12 16
        
17
        /// <summary>
18
        /// Instance of an HTTP client the is used to send data off to the server 
19
        /// </summary>
13 20
        public IHttpClient _client;
14 21

  
22
        /// <summary>
23
        /// Flag used to stop the client (periodical retrieval from the cache)
24
        /// </summary>
15 25
        public bool ClientRunning;
16 26

  
27
        /// <summary>
28
        /// Number of milliseconds after which the class tries to resend failed payloads to the server.
29
        /// </summary>
17 30
        private readonly uint _retryPeriod;
31
        
32
        /// <summary>
33
        /// Maximum number of entries (payloads) that can be sent to the server within one period (_retryPeriod).
34
        /// </summary>
18 35
        private readonly uint _maxEntries;
36
        
37
        /// <summary>
38
        /// Maximum number of failed payloads to be kept in the file-based cache (FIFO - when the maximum number is reached)
39
        /// </summary>
19 40
        private readonly uint _maxRetries;
20 41
        private readonly IPersistentQueue _cache;
21 42

  
43
        /// <summary>
44
        /// Creates an instance of the class.
45
        /// </summary>
46
        /// <param name="url">IP address of the server (url in case a DNS server is being used)</param>
47
        /// <param name="port">port that the API is running on</param>
48
        /// <param name="path">path of the API e.g. /api/v1/lg-logs</param>
49
        /// <param name="retryPeriod">number of milliseconds after which the class tries to resend failed payloads to the server</param>
50
        /// <param name="maxEntries">maximum number of entries (payloads) that can be sent to the server within one period</param>
51
        /// <param name="maxRetries">maximum number of failed payloads to be kept in the file-based cache</param>
52
        /// <param name="cache">instance of a persistent cache for storing failed payloads</param>
22 53
        public ApiClient(string url, uint port, string path, uint retryPeriod, uint maxEntries, uint maxRetries, IPersistentQueue cache) {
54
            // Construct the entire path to the API.
23 55
            var uri = $"{url}:{port}{path}";
56
            
57
            // Store the values into class variables.
24 58
            _retryPeriod = retryPeriod;
25 59
            _maxEntries = maxEntries;
26 60
            _maxRetries = maxRetries;
61
            _cache = cache;
27 62

  
63
            // Create an instance of a HttpClient which takes care of
64
            // establishing a connection to the server;
28 65
            _client = new HttpClient(uri);
29
            _cache = cache;
30 66
        }
31 67

  
68
        /// <summary>
69
        /// Sends a payload to the server (API).
70
        /// </summary>
71
        /// <param name="payload">instance of a payload to be sent off to the server</param>
32 72
        public async Task SendPayloadAsync(Payload payload) {
33 73
            try {
74
                // Create an instance of Stopwatch (to measure how much
75
                // the action took).
34 76
                Stopwatch stopWatch = new();
35
                stopWatch.Start();
36 77
                
78
                // Send the payload to the server.
79
                stopWatch.Start();
37 80
                var response = await _client.PostAsJsonAsync(payload);
38 81
                stopWatch.Stop();
82
                
83
                // Create a log message.
39 84
                CreateRequestLog(payload, response, stopWatch.ElapsedMilliseconds);
40

  
85
                
86
                // Make sure the request was successful.
41 87
                response.EnsureSuccessStatusCode();
42 88
            } catch (Exception e) {
43 89
                Program.DefaultLogger.Error($"Failed to send {payload} to the server. Due to: {e.Message}");
......
45 91
            }
46 92
        }
47 93

  
94
        /// <summary>
95
        /// Creates a request log message.
96
        /// </summary>
97
        /// <param name="payload">payload involved in the process of sending data to the server</param>
98
        /// <param name="response">response from the server</param>
99
        /// <param name="durationMs">duration in milliseconds (how much time it took to send off the payload)</param>
48 100
        private static void CreateRequestLog(Payload payload, HttpResponseMessage response, long durationMs) {
101
            // Create the log message.
49 102
            var responseToLog = new {
50 103
                statusCode = response.StatusCode,
51 104
                content = response.Content,
52 105
                headers = response.Headers,
53 106
                errorMessage = response.RequestMessage,
54 107
            };
55

  
108
            
109
            // Log the message using the logger defined in Program (main class).
56 110
            Program.DefaultLogger.Info($"Request completed in {durationMs} ms,\n" +
57
                                 $"Request body: {payload},\n" +
58
                                 $"Response: {responseToLog}");
111
                                       $"Request body: {payload},\n" +
112
                                       $"Response: {responseToLog}");
59 113
        }
60 114
        
115
        /// <summary>
116
        /// Resends unsuccessful payloads to the server.
117
        /// </summary>
61 118
        private async Task ResendPayloadsAsync() {
119
            // Calculate the maximum number of payloads to be sent to the server.
62 120
            var numberOfPayloadsToResend = Math.Min(_maxRetries, _cache.EstimatedCountOfItemsInQueue);
121
            
122
            // Create a list for those payloads
63 123
            var payloads = new List<Payload>();
124
            
125
            // Retrieve the payloads from the cache.
64 126
            if (numberOfPayloadsToResend > 0) {
127
                // Open up a session to the cache.
65 128
                using var session = _cache.OpenSession();
129
                
130
                // Pop out payloads, deserialize them, and store them into the list.
66 131
                for (var i = 0; i < numberOfPayloadsToResend; i++) {
67 132
                    var rawBytes = session.Dequeue();
68 133
                    var payload = JsonSerializer.Deserialize<Payload>(rawBytes);
......
70 135
                        payloads.Add(payload);
71 136
                    }
72 137
                }
138
                // Flush the changes.
73 139
                session.Flush();
74 140
            }
75

  
141
            
142
            // If there are some payloads to be resent to the server.
76 143
            if (payloads.Count > 0) {
77 144
                Program.DefaultLogger.Debug($"ResendPayloadAsync -> {payloads.Count} unsent payloads");
78 145
                var tasks = new List<Task>();
146
                
147
                // Create a separate task for each payload - resend them to the server.
79 148
                foreach (var payload in payloads) {
80 149
                    Program.DefaultLogger.Info($"Resending {payload}.");
81 150
                    tasks.Add(SendPayloadAsync(payload));
82 151
                }
152
                // Wait until all tasks are finished. 
83 153
                await Task.WhenAll(tasks);
84 154
            }
85 155
        }
86 156
        
157
        /// <summary>
158
        /// Stores a failed payload into a persistent cache.
159
        /// </summary>
160
        /// <param name="payload"></param>
87 161
        private void CachePayload(Payload payload) {
88 162
            Program.DefaultLogger.Info($"Storing {payload} into the cache.");
163
            
164
            // Number of payloads stored in the cache.
89 165
            var numberOfCachedPayloads = _cache.EstimatedCountOfItemsInQueue;
166
            
167
            // Open up a session to the cache.
90 168
            using var session = _cache.OpenSession();
169
            
170
            // If the cache is "full", make room for the latest failed
171
            // payload by discarding the oldest one.
91 172
            if (numberOfCachedPayloads >= _maxEntries) {
92 173
                session.Dequeue();
93 174
            }
175
            
176
            // Store the payload into the cache.
94 177
            var payloadJson = JsonSerializer.Serialize(payload);
95 178
            session.Enqueue(Encoding.UTF8.GetBytes(payloadJson));
179
            
180
            // Flush the changes.
96 181
            session.Flush();
97 182
        }
98 183

  
184
        /// <summary>
185
        /// Runs the periodical retrieval of failed payloads stored
186
        /// in a file-based cache. This method is instantiated as a thread.
187
        /// </summary>
99 188
        public async void Run() {
100 189
            Program.DefaultLogger.Info("Api Client thread has started");
190
            
191
            // Keep the thread running.
101 192
            ClientRunning = true;
193
            
194
            // Keep resending failed payloads to the server.
102 195
            while (ClientRunning) {
103 196
                await ResendPayloadsAsync();
104 197
                Thread.Sleep((int) _retryPeriod);

Také k dispozici: Unified diff