20131119

Android 4.4 KitKat WebView 이슈 정리

KitKat 뜨고 테스트 단말 Nexus 5 샀는데 앱 WebView가 다 깨짐. 원인은 알다시피 Chromium 기반으로 바뀐거.

걸린 지점들 메모.

1. User-Agent 바뀜
// 4.3
Mozilla/5.0 (Linux; U; Android 4.3; ...) AppleWebKit/534.30 ...
// 4.4
Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/KRT16M) AppleWebKit/537.36 ... Chrome/30.0.0.0
서버에서 UA로 OS 분기하던 레거시 코드 몇 개가 오작동. "AppleWebKit/534"를 정규식으로 잡던 부분 다시 뜯어야 했다.

2. loadUrl("javascript:...") 가 void
그전엔 native에서 js 인보크하고 return value 못 받는걸 당연히 알고 있었는데, 4.4부터는 evaluateJavascript() 라는 새 API가 생겼음. callback으로 결과 받을 수 있음. 근데 4.4 미만에서는 안되니까 SDK 버전 분기 필요.

if (Build.VERSION.SDK_INT >= 19) {
    webView.evaluateJavascript("getToken()", new ValueCallback<String>() {
        public void onReceiveValue(String value) { ... }
    });
} else {
    webView.loadUrl("javascript:getToken()");
}

3. file:// 에서 XHR 막힘
로컬 html에서 json 불러오던게 CORS로 막힘. setAllowFileAccessFromFileURLs(true) 호출해야 되는데 보안상 찝찝함. 결국 assets 대신 서버에서 받아오는 쪽으로 바꿈.

기타 CSS viewport 계산이 살짝 달라져서 레이아웃 몇개 깨짐. 이건 일일이 잡는 수밖에...

20131110

PHP 5.5 OPcache 도입 후기

회사 서버 PHP 5.4.17 → 5.5.5 올리면서 APC 떼고 OPcache 붙인 기록. 일단 메모.

배경부터. 5.5에선 Zend OPcache가 번들로 들어오기 시작해서 APC가 사실상 단종 수순. APC 소스는 5.5 대응을 멈췄고, apcu 라는 user-cache 전용 포크가 따로 나왔다. 지금까지는 apc.stat=0 + user cache로 섞어 썼는데 앞으로는 opcode 쪽은 OPcache, user cache는 apcu 이렇게 분리해야 함.

php.ini 세팅은 일단 이렇게.

zend_extension=opcache.so
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=0
opcache.save_comments=1

max_accelerated_files=4000 — 컴포저 깔린 프로젝트는 파일 수가 금방 늘어남. find . -name "*.php" | wc -l 찍어보니 우리 레포 기준 2,900 정도 나와서 여유있게 4000. 이 값이 실제 파일 수보다 작으면 LRU로 밀려나면서 컴파일 다시 타서 TTFB 튀는 순간이 있다. opcache status에 max_cached_keys 소진되면 로그 남기는게 정석인데 일단은 수동으로 가끔 봄.

interned_strings_buffer=8MB — 문자열 인터닝(동일 리터럴 재사용) 풀 크기. 기본 4MB인데 심포니/라라벨 계열은 로케일/라우트/설정 문자열이 많아서 4로는 부족함. 이게 꽉 차면 opcache 전체 동작이 이상해지는 버그 리포트가 몇 건 있어서 약간 여유있게.

revalidate_freq=60 — 처음엔 2초로 뒀다. 근데 배포 직후 첫 히트에서 파일 stat 다 부르는게 아까워서 60초로 늘림. 대신 배포 스크립트 끝에 curl http://127.0.0.1/opcache_reset.php 찔러서 강제 flush. reset 트리거용 엔드포인트는 localhost 바인딩만 허용하도록 listen 막아둠. FPM reload 대안은 graceful하긴 한데 worker 죽을 때 마침 긴 쿼리 물려있는 요청이 애매하게 끊겨서 싫었다.

fast_shutdown=1 — 요청 끝날 때 PHP가 일반적인 destructor 해제 순서 대신 Zend MM 블록을 한번에 날리는 모드. 마이크로 벤치에서 request/sec 5~8% 정도 이득 봄. 단점이라면 shutdown 중 에러가 잘 안 보이게 되는데, 개발 서버만 0 두고 프로덕션은 1로.

성능 체감은 APC 시절 대비 비슷하거나 살짝 나음. ab로 대충 재보면 홈 페이지 requests/sec 340 → 365 정도. 극적 차이는 아님. 대신 메모리 조각화가 훨씬 덜하다. APC는 user cache랑 opcode cache가 같은 공간 공유해서 fragmentation이 40% 넘게 튀던게, 분리하고 나니 OPcache 쪽은 거의 2% 이하 유지. opcache_get_status() 찍어보면 wasted_memory가 눈에 띄게 낮다.

memory_consumption 128MB로 잡았는데 실제 점유는 48~52MB 수준. 일단 여유있게 두고 나중에 줄일 예정. 나중에 64MB로 줄이면 interned strings랑 충돌해서 warning 나올 수 있으니 그때 다시 볼것.

남은 숙제: apcu 별도 컴파일해서 user cache 복구 + opcache.huge_code_pages 실험. 얘는 Transparent Huge Pages 기반이라 커널쪽 설정이 엮여서 좀 귀찮긴 한데 TLB 미스 줄여서 2~3% 더 나온다는 얘기가 있음. 담주에.