NSURLCache кэширует случайные ответы, которые не следует кэшировать

Я реализую приложение, которое выполняет много сетевых вызовов для остальных API, которые мы также контролируем. Недавно мы решили внедрить заголовки кэширования на стороне сервера, чтобы сэкономить ценное сетевое и серверное время. Поскольку мы заранее не знаем, как долго данные будут действительны, мы не отправляем заголовки Cache-control: max-age или Expires, все, что мы делаем, это отправляем заголовок Last-Modified вместе с E-tag, поэтому мы всегда обращаемся к серверу, но в большинстве случаев ответы приходят довольно быстро. времена с 304. Сначала все работало нормально, многие запросы кэшировались. Однако в приложении возникают случайные ошибки данных из-за кэширования.

По какой-то причине я не могу понять, в какой-то момент запросы локально кэшируются и используются как "обновляемые" данные без обращения к серверу, хотя на самом деле это не так. Проблема сохраняется до тех пор, пока не пройдет некоторое время. Затем все снова идет на сервер нормально, точно так же, как с заголовком cache-control, но без него!. Итак, мой вопрос:


Как NSURLCache вместе с NSURLConnection решить, что конкретный запрос не нужно отправлять в онлайн, если первоначальный запрос не содержал заголовков Cache-control: max-age или Expires? Кто-нибудь сталкивался с подобным эффектом? И как решить, не удаляя весь кеш?


Еще немного справочной информации:

  • Я использую AFNetworking, но он зависит от NSURLConnection, поэтому не думаю, что это что-то меняет
  • Используемый кеш — это экземпляр [NSURLCache sharedURLCache] по умолчанию.
  • Это запрос GET, и когда я проверяю заголовки из кэшированного ответа, я получаю следующее:

    po [response allHeaderFields]

    "Access-Control-Allow-Headers" = "Content-Type";
    "Access-Control-Allow-Methods" = "GET, POST, DELETE, PUT";
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Encoding" = gzip;
    "Content-Length" = 522;
    "Content-Type" = "application/json";
    Date = "Mon, 02 Sep 2013 08:00:38 GMT";
    Etag = "\"044ad6e73ccd45b37adbe1b766e6cd50c\"";
    "Last-Modified" = "Sat, 31 Aug 2013 10:36:06 GMT";
    Server = "nginx/1.2.1";
    "Set-Cookie" = "JSESSIONID=893A59B6FEFA51566023C14E3B50EE1E; Path=/rest-api/; HttpOnly";
    
  • Я не могу предсказать или воспроизвести, когда произойдет ошибка, поэтому решения, основанные на удалении кеша, не подходят.

  • Я использую iOS5+

person Angel G. Olloqui    schedule 02.09.2013    source источник
comment
Нет шансов, что есть проблема с синхронизацией часов между устройством и сервером? Спецификации говорят, что поведение клиента в этом случае не определено (на усмотрение клиента). Также вы пытались использовать код afnetworking, чтобы получить больше информации?   -  person Alex    schedule 07.09.2013
comment
проблема времени была моей первой мыслью, но я не смог найти повторяющийся сценарий (это происходит очень случайно). Во всяком случае, это произошло на устройствах с правильным временем, и серверная часть также работает правильно, так что это не так :( спасибо за подсказку!   -  person Angel G. Olloqui    schedule 11.09.2013


Ответы (3)


Как NSURLCache вместе с NSURLConnection может решить, что конкретный запрос не должен выходить в сеть, когда...

В разделе 13.2 RFC 2616 говорится:

Поскольку исходные серверы не всегда предоставляют явное время истечения срока действия, кэши HTTP обычно назначают эвристическое время истечения срока действия, используя алгоритмы, которые используют другие значения заголовка (например, время последнего изменения) для оценки вероятного времени истечения срока действия. Спецификация HTTP/1.1 не предоставляет конкретных алгоритмов, но налагает на их результаты наихудшие ограничения. Поскольку эвристические сроки действия могут поставить под угрозу семантическую прозрачность, их следует использовать с осторожностью, и мы рекомендуем исходным серверам предоставлять явные сроки действия, насколько это возможно.

Таким образом, система загрузки URL-адресов может решить, что кэшированные данные «достаточно свежие», даже если вы не указали конкретное время жизни данных.

Для достижения наилучших результатов вы должны попытаться указать определенное время жизни в заголовках ваших ответов. Если добавление такого заголовка невозможно, возможно, вместо этого вы могли бы изменить запрос. if-modified-since или cache-control могут помочь вам избежать кэширования данных.

person Caleb    schedule 10.09.2013
comment
Это кажется лучшим ответом на данный момент. Это не решает проблему, но, по крайней мере, абзац RFC 2616 объясняет, что я испытываю. Спасибо! - person Angel G. Olloqui; 11.09.2013

Согласно утверждению «в какой-то момент запросы локально кэшируются и используются как «обновляемые» данные без обращения к серверу», я почти уверен, что ваши запросы кэшируются в памяти.

NSURLCache кэширует данные в памяти. не на диске. Итак, позвольте мне объяснить, что может происходить с вами.

Вы запускаете приложение. Делает вызов веб-службы, он извлекает данные с сервера, вы снова делаете вызов, и он извлекает ответ из памяти, не выполняя вызов на сервер, и отображает вам результат.

Вы покидаете приложение на какое-то время или перезапускаете приложение. Он проверяет, находятся ли данные в памяти. Если он недоступен, он снова обращается к серверу и повторяет то же поведение.

Я бы порекомендовал вам написать собственное кэширование диска для того же, вместо того, чтобы полагаться на NSURLConnection и NSUrlCache. потому что некоторые политики кэширования до сих пор не реализованы Apple.

person Vaibhav Gautam    schedule 10.09.2013
comment
Спасибо, Вайлбхав. Как я уже сказал, на самом деле проблема с кэшем, но дело не в том, что что-то не кэшируется, а наоборот: кэшируется то, что не должно кэшироваться. Кроме того, он сохраняется на диск, но это ожидаемо, потому что в iOS5 нейронный кеш сохраняется на диск. - person Angel G. Olloqui; 11.09.2013

убедитесь, что для политики кэширования NSURLRequest установлено значение NSURLRequestReturnCacheDataElseLoad.

здесь, если вы используете AFNetworking в AFHTTPClient.m, вы можете переопределить метод

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                  path:(NSString *)path
                            parameters:(NSDictionary *)parameters

замените строку 470 на эту

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:15];

что вы на самом деле делаете, так это сообщаете запросу о загрузке кеша, если сервер не обновлен. Если сервер обновлен, он будет игнорировать кеш и загружать содержимое с сервера.

К вашему сведению: NSURLCache хранит данные в памяти. Если вы хотите сохранить данные на диске, вы можете использовать мой класс здесь

https://github.com/shoeb01717/Disc-Cache-iOS

person Shoeb Amin    schedule 10.09.2013
comment
Спасибо, но, как объяснено, проблема не в том, что информация не случайно, а в совершенно противоположном: она кэшируется, когда не должна. К тому же iOS5 кеширует на диск, поэтому реализация моего собственного класса, вероятно, мало поможет, хотя я попробую. - person Angel G. Olloqui; 11.09.2013
comment
@shoeb: к вашему сведению, поскольку ios5 NSURLCACHE позволяет вам выбирать диск или память, а также выделять размер. - person Alex; 12.09.2013
comment
Спасибо, я знал о функциях iOS5, но класс, который я сделал, также позволит вам вернуть кеш из основного пакета приложения. - person Shoeb Amin; 12.09.2013