Как получить последние измененные события IObservable<IObservable <T >>?
В моей системе много объектов статуса - статус соединения, загрузка процессора, зарегистрированные пользователи и так далее. Все такие события объединены в один наблюдаемый поток.
Я хочу сделать утилиту администратора, чтобы показать фактическое состояние системы и показать все эти счетчики.
Как я могу создать наблюдаемую информацию, которая будет иметь список последних измененных значений всех счетчиков?
Вот мраморная диаграмма, которую я хочу иметь:
s1 (cpu): -s1_v1----s1_v1---s1_v2
s2 (users count): --s2_v1--s2_v1---------s2_v2
s3 (some cat purr/sec) ----s3_v1----s3_v1----s3_v1
flatten sequence: s1_v1-s2_v1-s3_v1-s2_v1-s1_v1-s3_v1-s1_v2-s3_v1-s2_v2
желаемый результат:
s1_v1|s1_v1|s1_v1|s1_v2|s1_v2
s2_v1|s2_v1|s2_v1|s2_v2
s3_v1|s3_v1|s3_v1
Пока я могу к этой реализации:
public class StatusImplementation
{
public static IObservable<IDictionary<TKey, TValue>> Status<TKey, TValue>(
params IObservable<KeyValuePair<TKey, TValue>>[] observables)
{
var uniqueObservables = observables
.Select(x => x.Publish().RefCount().DistinctUntilChanged());
return Observable.Create<IDictionary<TKey, TValue>>(o =>
{
var compositeDisposable = new CompositeDisposable();
var dictionary = new Dictionary<TKey, TValue>();
foreach (var uniqueObservable in uniqueObservables)
{
var disposable = uniqueObservable.Subscribe(x =>
{
if (dictionary.ContainsKey(x.Key) && !dictionary[x.Key].Equals(x.Value))
{
var newDictionary = new Dictionary<TKey, TValue>(dictionary);
newDictionary[x.Key] = x.Value;
dictionary = newDictionary;
}
else
{
dictionary.Add(x.Key, x.Value);
}
o.OnNext(dictionary);
});
compositeDisposable.Add(disposable);
}
return compositeDisposable;
});
}
}
И вот пример использования:
var f1 = Observable.Interval(TimeSpan.FromMilliseconds(1000))
.Select(x => new KeyValuePair<string, long>("event 1", x));
var f2 = Observable.Interval(TimeSpan.FromMilliseconds(1200))
.Select(x => new KeyValuePair<string, long>("event 2", x));
var f3 = Observable.Interval(TimeSpan.FromMilliseconds(1250))
.Select(x => new KeyValuePair<string, long>("event 3", x));
var combined = f1.Merge(f2).Merge(f3);
StatusImplementation.Status(f1, f2, f3)
.Select(x => string.Join(", ", x.ToList()))
.Dump("\tstatus");
combined.Dump("normal");
И функция Dump (из великой книги Ли Кэмпбелла):
public static void Dump<T>(this IObservable<T> source, string name)
{
source.Subscribe(
i => Console.WriteLine("{0}-->{1}", name, i),
ex => Console.WriteLine("{0} failed-->{1}", name, ex.Message),
() => Console.WriteLine("{0} completed", name));
}
Таким образом, вопрос: есть ли лучший способ реализовать эту функциональность? Вероятно, не используя словарь внутри наблюдаемого?
Спасибо.
2 ответа
Так что, если вы начнете с combined
наблюдаемый - который может быть получен из любого количества исходных наблюдаемых - тогда вы можете сделать это:
var query =
combined
.Scan(
new Dictionary<string, long>() as IDictionary<string, long>,
(d, kvp) =>
{
var d2 = new Dictionary<string, long>(d) as IDictionary<string, long>;
d2[kvp.Key] = kvp.Value;
return d2;
});
Это вернет серию объектов словаря для каждого значения, созданного combined
наблюдаемым. Каждый объект словаря будет отдельным экземпляром - если бы был возвращен один и тот же экземпляр, вы бы когда-либо изменили значения, которые могут вызвать проблемы с многопоточностью.
Ты можешь использовать Observable.CombineLatest
, который будет выдавать последние значения из всех наблюдаемых при каждом поступлении нового значения. Тогда вам не нужно использовать словарь.
var f1 = Observable.Interval(TimeSpan.FromMilliseconds(1000))
.Select(x = > new KeyValuePair < string, long > ("event 1", x));
var f2 = Observable.Interval(TimeSpan.FromMilliseconds(1200))
.Select(x = > new KeyValuePair < string, long > ("event 2", x));
var f3 = Observable.Interval(TimeSpan.FromMilliseconds(1250))
.Select(x = > new KeyValuePair < string, long > ("event 3", x));
var combined = f1.Merge(f2).Merge(f3);
Observable.CombineLatest(f1, f2, f3)
.Select(x = > string.Join(", ", x.ToList()))
.Dump("\tstatus");
combined.Dump("normal");