Ответ 1
Вы в значительной степени указали свои варианты. Ответ, который вы ищете, зависит от того, как вы хотите использовать значения, хранящиеся в контексте.
context.Context
- неизменный объект, "расширение" его с помощью пары ключ-значение возможно только путем создания его копии и добавление нового ключевого значения в копию (что делается под капотом, с помощью context
).
Хотите, чтобы дополнительные обработчики имели доступ ко всем значениям ключа прозрачным способом? Затем добавьте все в цикл, используя всегда контекст последней операции.
Здесь следует отметить, что context.Context
не использует map
под капотом для хранения пар ключ-значение, что может показаться неожиданным сначала, но если вы думаете об этом, оно должно быть неизменным и безопасный для одновременного использования.
Использование map
Так, например, если у вас много пар ключ-значение и вам нужно быстро найти значения по клавишам, добавление каждого из них приведет к созданию context
, метод Value()
будет медленным. В этом случае лучше, если вы добавите все свои пары ключ-значение в виде единственного значения map
, к которому можно получить доступ через Context.Value()
, и каждое значение в нем может быть запрошено связанным ключом в O(1)
времени. Знайте, что это не будет безопасно для одновременного использования, хотя, поскольку карта может быть изменена из параллельных goroutines.
Использование struct
Если вы используете большое значение struct
, имеющее поля для всех пар ключ-значение, которые вы хотите добавить, это также может быть жизнеспособным вариантом. Доступ к этой структуре с помощью Context.Value()
вернет вам копию структуры, поэтому было бы безопасно для одновременного использования (каждый goroutine мог получить только другую копию), но если у вас много пар ключ-значение, это приведет к ненужная копия большой структуры каждый раз, когда кому-то требуется одно поле.
Использование гибридного решения
Гибридное решение может состоять в том, чтобы поместить все ваши пары ключ-значение в map
и создать структуру-оболочку для этой карты, скрыв map
(нераспределенное поле) и предоставить только получатель для сохраненных значений на карте. Добавляя только эту оболочку в контекст, вы сохраняете безопасный параллельный доступ для нескольких goroutines (map
не экспортируется), но не нужно копировать большие данные (map
значения - это небольшие дескрипторы без данных ключа), и тем не менее он будет быстрым (так как в конечном счете вы индексируете карту).
Вот как это могло бы выглядеть:
type Values struct {
m map[string]string
}
func (v Values) Get(key string) string {
return v.m[key]
}
Используя его:
v := Values{map[string]string{
"1": "one",
"2": "two",
}}
c := context.Background()
c2 := context.WithValue(c, "myvalues", v)
fmt.Println(c2.Value("myvalues").(Values).Get("2"))
Выход (попробуйте на Go Playground):
two
Если производительность не является критичной (или у вас относительно мало пар ключ-значение), я бы добавил их отдельно.