负责公司的基础数据扫描采集.
这部分对于系统的可用性基本是100%
所以做了很多高可用的方案
前置准备
在对APP进行高可用实施之前,我们需要准备:
1.核心域名多个降级[一主多备]
最基础的要求,必须!!!
能够支持云端下发 & 本地动态切换(蓝-绿发布 & 灰度 & UAT)
2.多个CDN
每个域名 都使用不同CDN,避免因CDN节点故障导致服务不可用(出现过因CDN节点异常的生产故障)
3.多个部署网络机房
每个域名 部署在不同地域网络机房(出现过主干线因施工被挖断的生产故障)
===========================================================================
核心思想就是: 同城保活、异地灾备再结合服务检测、异常动态切换
以上都是实际生产故障的血泪经验总结.
那么下面简单说一下几种APP的高可用实施方案.
说明
域名的调用优先级是基于云端下发的配置来了
云端下发的多域名配置会缓存在内存.
每次网络请求,都遵循
多域名[0] --> 多域名[1] --> 多域名[2] …
动态多域名下发
这个是必备的,也没什么好说的,略…
CDN节点异常 & CDN选优
启动一个服务,定时对服务进行 ping,出现异常的,直接切换到其他备用域名,直至主服务恢复
贴一下工具类:
public static PingResult customCMD(String host,String command){
PingResult result = new PingResult();
try {
result.host = host;
Process process = Runtime.getRuntime().exec(command+host);
process.waitFor();
result.success = true;
InputStreamReader reader = new InputStreamReader(process.getInputStream());
BufferedReader br = new BufferedReader(reader);
List<String> echo = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
echo.add(line);
}
PingResultParser.parsePingContent(result, echo);
} catch (Exception e) {
e.printStackTrace();
result.success = false;
result.ping_time = -1.0;
}
return result;
}
public static class PingResult {
public String host;
public boolean success;
public double ping_time;
}
private static class PingResultParser {
static void parse(PingResult result, List<String> echo) {
// PING www.a.shifen.com (xxx.xxx.xxx.xx) 56(84) bytes of data.
// 64 bytes from xxx.xxx.xxx.xx: icmp_seq=1 ttl=52 time=21.5 ms
//
// --- www.a.shifen.com ping statistics ---
// 1 packets transmitted, 1 received, 0% packet loss, time 0ms
// rtt min/avg/max/mdev = 21.585/21.585/21.585/0.000 ms
if (echo.size() == 6) {
result.ping_time = getPingTime(echo.get(1));
}
}
static double getPingTime(String line) {
String[] block = line.split(" ");
String timeStr = block[6].split("=")[1];
return ValueUtils.parseDouble(timeStr, 0);
}
static void parsePingContent(PingResult result, List<String> echo) {
for (String line : echo) {
//ping的内容
if (line.contains("icmp_seq=") && line.contains("ttl=") && line.contains("time=")) {
String[] block = line.split(" ");
String timeStr = block[6].split("=")[1];
Log.d(TAG, "IP:" + result.host + ",耗时为:" + timeStr + " ms");
}
if (line.contains("rtt min/avg/max/mdev")) {
String timeResult = line.split("=")[1];
String[] split = timeResult.split("/");
//取平均时间
result.ping_time = Double.parseDouble(split[1]);
Log.d(TAG,"域名切换检测==="+result.host+",ping为:"+result.ping_time);
}
}
}
}
开启服务,定期对每个域名跑一边就行,可以使用这个命令
**ping -c 3 -w 2 **
然后基于结果,动态的对retrofit的BASEURL进行切换即可.
请求异常动态切换域名
自定义拦截器,在碰到请求异常时,动态切换至下一个域名自动重新请求,直至所有域名重试完成
如果只需要简单的重试,通过获取**chain.request()**的url,判断host是否为 多域名,是否支持动态切换,然后动态切换,重试就行.
如果不理解的话,建议看一下okhttp的相关源码对于拦截器的实现.
我在这个基础上,额外新增了自定义注解.
主要是用来对于特定服务,实行额外的自定义重试
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RetryCount {
int extraCount();
}
不过需要注意的是,需要retrofit版本在2.5.0 及以上才行
request = chain.request();
//获取重试请求的自定义注解
Invocation tag = request.tag(Invocation.class);
Method method = tag != null ? tag.method() : null;
RetryCount retryCount = method != null ? method.getAnnotation(RetryCount.class) : null;
最小化上云
这种方案是另外一个方向了
大概是这样
通过动态生效的 功能开关进行控制.
当发现主服务异常时.主动将流量切换至 阿里云 / 华为 等云服务.
先保障用户能够正常使用.先把数据上传上来.
再通过 积压监控 + webhook机器人告警等方式.
去进行消费,解析会主服务.
End
好久没写Android方面的博客了.