cn.leancloud.core.AppRouter Maven / Gradle / Ivy
package cn.leancloud.core;
import cn.leancloud.AVLogger;
import cn.leancloud.cache.SystemSetting;
import cn.leancloud.network.DNSDetoxicant;
import cn.leancloud.service.AppAccessEndpoint;
import cn.leancloud.service.AppRouterService;
import cn.leancloud.service.RTMConnectionServerResponse;
import cn.leancloud.utils.LogUtil;
import cn.leancloud.utils.StringUtil;
import com.alibaba.fastjson.JSON;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.fastjson.FastJsonConverterFactory;
import java.util.concurrent.TimeUnit;
public class AppRouter {
private static final AVLogger LOGGER = LogUtil.getLogger(AppRouter.class);
private static final String APP_ROUTER_HOST = "https://app-router.leancloud.cn";
private static final AppRouter INSTANCE = new AppRouter();
public static AppRouter getInstance() {
return INSTANCE;
}
/**
* 华北区 app router 请求与结果
* https://app-router.leancloud.cn/2/route?appId=EDR0rD8otnmzF7zNGgLasHzi-MdYXbMMI
* {
* ttl: 3600,
* stats_server: "nlqwjxku.stats.lncld.net",
* rtm_router_server: "nlqwjxku.rtm.lncld.net",
* push_server: "nlqwjxku.push.lncld.net",
* engine_server: "nlqwjxku.engine.lncld.net",
* api_server: "nlqwjxku.api.lncld.net",
* }
*
* 华东区 app router 请求与结果
* https://app-router.leancloud.cn/2/route?appId=qwTQb5S80beMUMGg3xtHsEka-9Nh9j0Va
* {
* ttl: 3600,
* stats_server: "qwtqb5s8.stats.lncldapi.com",
* rtm_router_server: "qwtqb5s8.rtm.lncldapi.com",
* push_server: "qwtqb5s8.push.lncldapi.com",
* engine_server: "qwtqb5s8.engine.lncldapi.com",
* api_server: "qwtqb5s8.api.lncldapi.com",
* }
*
* 美国区 app router 请求与结果
* https://app-router.leancloud.cn/2/route?appId=EDR0rD8otnmzF7zNGgLasHzi-MdYXbMMI
* {
* ttl: 3600,
* stats_server: "us-api.leancloud.cn",
* rtm_router_server: "router-a0-push.leancloud.cn",
* push_server: "us-api.leancloud.cn",
* engine_server: "us-api.leancloud.cn",
* api_server: "us-api.leancloud.cn",
* }
*/
private static final String DEFAULT_SERVER_HOST_FORMAT = "https://%s.%s.%s";
private static final String DEFAULT_SERVER_API = AVOSService.API.toString();
private static final String DEFAULT_SERVER_STAT = AVOSService.STATS.toString();
private static final String DEFAULT_SERVER_ENGINE = AVOSService.ENGINE.toString();
private static final String DEFAULT_SERVER_PUSH = AVOSService.PUSH.toString();
private static final String DEFAULT_SERVER_RTM_ROUTER = AVOSService.RTM.toString();
private static final String DEFAULT_REGION_EAST_CHINA = "lncldapi.com";
private static final String DEFAULT_REGION_NORTH_CHINA = "lncld.net";
private static final String DEFAULT_REGION_NORTH_AMERICA = "lncldglobal.com";
public static AVOSCloud.REGION getAppRegion(String applicationId) {
if (StringUtil.isEmpty(applicationId)) {
return AVOSCloud.REGION.NorthChina;
}
if (applicationId.endsWith("-MdYXbMMI")) {
return AVOSCloud.REGION.NorthAmerica;
}
if (applicationId.endsWith("-9Nh9j0Va")) {
return AVOSCloud.REGION.EastChina;
}
return AVOSCloud.REGION.NorthChina;
}
private Retrofit retrofit = null;
private AppAccessEndpoint defaultEndpoint = null;
private AppAccessEndpoint customizedEndpoint = new AppAccessEndpoint();
protected AppRouter() {
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.dns(new DNSDetoxicant())
.build();
retrofit = new Retrofit.Builder()
.baseUrl(APP_ROUTER_HOST)
.addConverterFactory(FastJsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient)
.build();
}
protected AppAccessEndpoint buildDefaultEndpoint(String appId) {
AppAccessEndpoint result = new AppAccessEndpoint();
String appIdPrefix = appId.substring(0, 8).toLowerCase();
AVOSCloud.REGION region = getAppRegion(appId);
String lastHost = "";
switch (region) {
case NorthChina:
lastHost = DEFAULT_REGION_NORTH_CHINA;
break;
case EastChina:
lastHost = DEFAULT_REGION_EAST_CHINA;
break;
case NorthAmerica:
lastHost = DEFAULT_REGION_NORTH_AMERICA;
break;
default:
LOGGER.w("Invalid region");
break;
}
result.setApiServer(String.format(DEFAULT_SERVER_HOST_FORMAT, appIdPrefix, DEFAULT_SERVER_API, lastHost));
result.setEngineServer(String.format(DEFAULT_SERVER_HOST_FORMAT, appIdPrefix, DEFAULT_SERVER_ENGINE, lastHost));
result.setPushServer(String.format(DEFAULT_SERVER_HOST_FORMAT, appIdPrefix, DEFAULT_SERVER_PUSH, lastHost));
result.setRtmRouterServer(String.format(DEFAULT_SERVER_HOST_FORMAT, appIdPrefix, DEFAULT_SERVER_RTM_ROUTER, lastHost));
result.setStatServer(String.format(DEFAULT_SERVER_HOST_FORMAT, appIdPrefix, DEFAULT_SERVER_STAT, lastHost));
result.setTtl(36000 + System.currentTimeMillis() / 1000);
return result;
}
private Observable fetchServerFromRemote(final String appId, final AVOSService service) {
return fetchServerHostsInBackground(appId).map(new Function() {
@Override
public String apply(AppAccessEndpoint appAccessEndpoint) throws Exception {
String result = "";
switch (service) {
case API:
result = appAccessEndpoint.getApiServer();
break;
case ENGINE:
result = appAccessEndpoint.getEngineServer();
break;
case PUSH:
result = appAccessEndpoint.getPushServer();
break;
case RTM:
result = appAccessEndpoint.getRtmRouterServer();
break;
case STATS:
result = appAccessEndpoint.getStatServer();
break;
default:
break;
}
if (!StringUtil.isEmpty(result) && !result.startsWith("http")) {
result = "https://" + result;
}
return result;
}
});
}
public void freezeEndpoint(final AVOSService service, String host) {
this.customizedEndpoint.freezeEndpoint(service, host);
}
public Observable getEndpoint(final String appId, final AVOSService service) {
return getEndpoint(appId, service, false);
}
private Observable getEndpoint(final String appId, final AVOSService service, boolean forceUpdate) {
String fixedHost = this.customizedEndpoint.getServerHost(service);
if (!StringUtil.isEmpty(fixedHost)) {
return Observable.just(fixedHost);
}
if (forceUpdate) {
// force to update from server.
return fetchServerFromRemote(appId, service);
}
if (null == this.defaultEndpoint) {
SystemSetting setting = AppConfiguration.getDefaultSetting();
String cachedResult = null;
if (null != setting) {
cachedResult = setting.getString(getPersistenceKeyZone(appId, true), appId, "");
}
if (!StringUtil.isEmpty(cachedResult)) {
defaultEndpoint = JSON.parseObject(cachedResult, AppAccessEndpoint.class);
long currentSeconds = System.currentTimeMillis() / 1000;
if (currentSeconds > defaultEndpoint.getTtl()) {
defaultEndpoint = null;
}
}
if (null == defaultEndpoint) {
defaultEndpoint = buildDefaultEndpoint(appId);
}
}
String result = null;
switch (service) {
case API:
result = this.defaultEndpoint.getApiServer();
break;
case ENGINE:
result = this.defaultEndpoint.getEngineServer();
break;
case PUSH:
result = this.defaultEndpoint.getPushServer();
break;
case RTM:
result = this.defaultEndpoint.getRtmRouterServer();
break;
case STATS:
result = this.defaultEndpoint.getStatServer();
break;
default:
break;
}
if (!StringUtil.isEmpty(result) && !result.startsWith("http")) {
result = "https://" + result;
}
return Observable.just(result);
// if (null != this.defaultEndpoint) {
// } else {
// return fetchServerFromRemote(appId, service);
// }
}
public Observable fetchServerHostsInBackground(final String appId) {
AppRouterService service = retrofit.create(AppRouterService.class);
Observable result = service.getRouter(appId);
if (AppConfiguration.isAsynchronized()) {
result = result.subscribeOn(Schedulers.io());
}
AppConfiguration.SchedulerCreator creator = AppConfiguration.getDefaultScheduler();
if (null != creator) {
result = result.observeOn(creator.create());
}
return result.map(new Function() {
@Override
public AppAccessEndpoint apply(AppAccessEndpoint appAccessEndpoint) throws Exception {
// save result to local cache.
LOGGER.d(appAccessEndpoint.toString());
AppRouter.this.defaultEndpoint = appAccessEndpoint;
AppRouter.this.defaultEndpoint.setTtl(appAccessEndpoint.getTtl() + System.currentTimeMillis() / 1000);
SystemSetting setting = AppConfiguration.getDefaultSetting();
if (null != setting) {
String endPoints = JSON.toJSONString(AppRouter.this.defaultEndpoint);
setting.saveString(getPersistenceKeyZone(appId, true), appId, endPoints);
}
return AppRouter.this.defaultEndpoint;
}
});
}
private Observable fetchRTMServerFromRemote(final String routerHost, final String appId,
final String installationId, int secure) {
LOGGER.d("fetchRTMServerFromRemote. router=" + routerHost + ", appId=" + appId);
Retrofit tmpRetrofit = retrofit.newBuilder().baseUrl(routerHost).build();
AppRouterService tmpService = tmpRetrofit.create(AppRouterService.class);
Observable result = tmpService.getRTMConnectionServer(appId, installationId, secure);
if (AppConfiguration.isAsynchronized()) {
result = result.subscribeOn(Schedulers.io());
}
AppConfiguration.SchedulerCreator creator = AppConfiguration.getDefaultScheduler();
if (null != creator) {
result = result.observeOn(creator.create());
}
return result.map(new Function() {
@Override
public RTMConnectionServerResponse apply(RTMConnectionServerResponse rtmConnectionServerResponse) throws Exception {
SystemSetting setting = AppConfiguration.getDefaultSetting();
if (null != rtmConnectionServerResponse && null != setting) {
rtmConnectionServerResponse.setTtl(rtmConnectionServerResponse.getTtl() + System.currentTimeMillis() / 1000);
String cacheResult = JSON.toJSONString(rtmConnectionServerResponse);
setting.saveString(getPersistenceKeyZone(appId, false), routerHost, cacheResult);
}
return rtmConnectionServerResponse;
}
});
}
public Observable fetchRTMConnectionServer(final String routerHost, final String appId,
final String installationId, int secure,
boolean forceUpdate) {
if (!forceUpdate) {
RTMConnectionServerResponse cachedResponse = null;
SystemSetting setting = AppConfiguration.getDefaultSetting();
if (null != setting) {
String cacheServer = setting.getString(getPersistenceKeyZone(appId, false), routerHost, "");
if (!StringUtil.isEmpty(cacheServer)) {
try {
cachedResponse = JSON.parseObject(cacheServer, RTMConnectionServerResponse.class);
long currentSeconds = System.currentTimeMillis()/1000;
if (currentSeconds > cachedResponse.getTtl()) {
// cache is out of date.
setting.removeKey(getPersistenceKeyZone(appId, false), routerHost);
cachedResponse = null;
}
if (null != cachedResponse) {
return Observable.just(cachedResponse);
}
} catch (Exception ex) {
cachedResponse = null;
setting.removeKey(getPersistenceKeyZone(appId, false), routerHost);
}
}
}
}
return fetchRTMServerFromRemote(routerHost, appId, installationId, secure);
}
protected String getPersistenceKeyZone(String appId, boolean forAPIEndpoints) {
if (forAPIEndpoints) {
return "com.avos.avoscloud.approuter." + appId;
} else {
return "com.avos.push.router.server.cache" + appId;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy