通过上面的代码我们可以得知Get数据之前之前先Load数据,Load其实就是在IDistributedCache中获取数据然后存储到了_store中,通过当前类源码可知_store是本地字典,也就是说Session直接获取的其实是本地字典里的数据。
private IDictionary<EncodedKey, byte[]> _store; 这里其实产生两点疑问:1.针对每个会话存储到IDistributedCache的其实都在一个Key里,就是以当前会话唯一标识为key的value里,为什么没有采取组合会话key单独存储。
2.每次请求第一次操作Session,都会把IDistributedCache里针对当前会话的数据全部加载到本地字典里,一般来说每次会话操作Session的次数并不会很多,感觉并不会节约性能。
接下来我们在再来查看另一个我们比较熟悉的方法Set方法
public void Set(string key, byte[] value) { if (value == null) { throw new ArgumentNullException(nameof(value)); } if (IsAvailable) { //存储的key是被编码过的 var encodedKey = new EncodedKey(key); if (encodedKey.KeyBytes.Length > KeyLengthLimit) { throw new ArgumentOutOfRangeException(nameof(key), Resources.FormatException_KeyLengthIsExceeded(KeyLengthLimit)); } if (!_tryEstablishSession()) { throw new InvalidOperationException(Resources.Exception_InvalidSessionEstablishment); } //是否修改过标识 _isModified = true; //将原始内容转换为byte数组 byte[] copy = new byte[value.Length]; Buffer.BlockCopy(src: value, srcOffset: 0, dst: copy, dstOffset: 0, count: value.Length); //将数据存储到本地字典_store _store[encodedKey] = copy; } }这里我们可以看到Set方法并没有将数据放入到存储系统,只是放入了本地字典里。我们再来看其他方法
public void Remove(string key) { Load(); _isModified |= _store.Remove(new EncodedKey(key)); } public void Clear() { Load(); _isModified |= _store.Count > 0; _store.Clear(); }这些方法都没有对存储系统DistributedCache里的数据进行操作,都只是操作从存储系统Load到本地的字典数据。那什么地方进行的存储呢,也就是说我们要找到调用_cache.Set方法的地方,最后在找到了Set方法,而且看这个方法名就知道是提交Session数据的地方
public async Task CommitAsync(CancellationToken cancellationToken = default) { //超过_ioTimeout CancellationToken将自动取消 using (var timeout = new CancellationTokenSource(_ioTimeout)) { var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken); //数据被修改过 if (_isModified) { if (_logger.IsEnabled(LogLevel.Information)) { try { cts.Token.ThrowIfCancellationRequested(); var data = await _cache.GetAsync(_sessionKey, cts.Token); if (data == null) { _logger.SessionStarted(_sessionKey, Id); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.SessionCacheReadException(_sessionKey, exception); } } var stream = new MemoryStream(); //将_store字典里的数据写到stream里 Serialize(stream); try { cts.Token.ThrowIfCancellationRequested(); //将读取_store的流写入到DistributedCache存储里 await _cache.SetAsync( _sessionKey, stream.ToArray(), new DistributedCacheEntryOptions().SetSlidingExpiration(_idleTimeout), cts.Token); _isModified = false; _logger.SessionStored(_sessionKey, Id, _store.Count); } catch (OperationCanceledException oex) { if (timeout.Token.IsCancellationRequested) { _logger.SessionCommitTimeout(); throw new OperationCanceledException("Timed out committing the session.", oex, timeout.Token); } throw; } } else { try { await _cache.RefreshAsync(_sessionKey, cts.Token); } catch (OperationCanceledException oex) { if (timeout.Token.IsCancellationRequested) { _logger.SessionRefreshTimeout(); throw new OperationCanceledException("Timed out refreshing the session.", oex, timeout.Token); } throw; } } } } private void Serialize(Stream output) { output.WriteByte(SerializationRevision); SerializeNumAs3Bytes(output, _store.Count); output.Write(IdBytes, 0, IdByteCount); //将_store字典里的数据写到Stream里 foreach (var entry in _store) { var keyBytes = entry.Key.KeyBytes; SerializeNumAs2Bytes(output, keyBytes.Length); output.Write(keyBytes, 0, keyBytes.Length); SerializeNumAs4Bytes(output, entry.Value.Length); output.Write(entry.Value, 0, entry.Value.Length); } }