> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# 旧服务优化思路
- URL: https://blog.hacking.pub/2019/03/12/jiu-fu-wu-you-hua/
- Published: 2019-03-12T13:20:46.000Z
- Updated: 2019-03-12T13:43:18.000Z
- Author: chainhelen

#### 高可用

讲互联网的技术，不能不说高可用；  
从某些书上面写的，主要通过“冗余+自动故障转”来实现的；  
对于以下分层，每一层上面都使用上面两种方式：

（1）【客户端层】到【反向代理层】  
（2）【反向代理层】到【站点层】  
（3）【站点层】到【服务层】  
（4）【服务层】到【缓存层】  
（5）【服务层】到【数据库读】  
（6）【服务层】到【数据库写】

#### 具体事例

记录一下具体实际工作中遇到一些烂项目，如何让它有基础的保障  
下面的事例中主要是服务层、缓存层、数据库，其中几处分类是有一定的重叠的

1. 监控  
服务本身除了提供基本的 api 延迟、qps、状态码统计，还需要加上对于依赖的监控  
主要核心的监控数据包含以下  
a）服务本身提供的能力：api延迟（每类path）、qps、状态码统计（包括400主要是参数问题例如被攻击、500、404主要是被扫描）  
b）服务间调用：http接口延迟+错误量统计、grpc接口延迟+错误量统计  
c）存储调用：redis（缓存）延迟+错误量统计、mysql（存储）延迟+错误量统计、es、hbase  
d）中间件调用：rabbitmq（或者nsq，消息中间件）的延迟+错误量统计  
e）服务占用资源：进程内存监控、进程cpu占用、协程数量、channel（go语言）、内核线程数量
2. 错误上报（注意不是日志收集）  
无论是采取nsq + es + kibana、还是简单的sentry，都应该将错误上报出来，不应该将错误（异常）掩盖，尽量让错误尽早的暴露出来
3. 降级  
对于依赖的服务，不能直接的信任，需要对多方面的降级  
a） 需要增加超时参数，不能过度信任依赖服务能在极短时间内有所返回  
b） 返回值不能完全信任，做好返回值是任意值的准备
4. 长链  
grpc 默认是http2，所以默认就是常量，主要需要注意负载均衡  
主要http调用需要使用http11（Connection：keep-alive）
5. 熔断  
对于依赖服务如果出现长时间不可用，需要做一些熔断  
如果发现了依赖服务已经不可用了（此时需要将错误信息上报上去）接着熔断防止危害扩散  
这里面需要注意一点就是，熔断是依赖服务的更强降级（我的理解）

```
之前遇到一个场景，A调用B（http接口，有降级），B服务的量级坑不住，调用疯狂超时，导致链接数（establish，time_out）暴涨，端口资源耗尽。而且这种场景下，采用长链只能起到优化，严重起来依然是灾难，最好保护手段是熔断       

```

1. 缓存  
a）  
对于稍大流量（5k）的服务，不是很建议使用ssdb，之前做压测的时候

官方推荐使用多线程，但是官方库却只有单线程的client

1. 在磁盘、cpu、内存都正常的情况下，32G SSDB磁盘数据，单线程（官方原生库）100000/s没有问题，但是我们要的是并发，只有一个线程是有问题的，5k的流量在排队等待一个ssdb线程返回数据是很快就会出现问题的（假设是10ms，没办法redis都是按μs做单位的）
2. 然后无论怎么调整协程数都是有问题的，链接数超过500过后，但线程的执行性能严重下降，最后设置了3000链接数，最终也只是支持18000并发

b）  
redis 的压测，基本跟网上差不多，5万/s 的qps 依然很快，这里面注意的是能到10万/s主要是利用了`pipeline`，如果撇开了`pipeline`在6万/s左右就会有点问题了

c）  
如果缓存量非常巨大的话，例如超过32G左右的时候，需要使用redis集群将数据分散在几个节点上面  
之前优化的时候主要使用了`twemproxy`，优劣势网上都有，就不多提了  
需要注意的是，对于单点`twemproxy`只能解决存储的问题，并不能解决并发读写性能问题  
之前遇到了一个场景，5万qps的redis操作，单点的`twemproxy`严重退化到平均操作延迟30\~40ms （其中get操作约3万，expire约3k）  
最后解决方案是又重新部署了同样配置的`twemproxy`，这里面注意两点，一个是设计key的时候，尽量保证`hash_tag`；第二使用是[nodes names](https://github.com/twitter/twemproxy/blob/master/notes/recommendation.md?ref=blog.hacking.pub#node-names-for-consistent-hashing)  
7\. 大单表  
a）读写分离  
主要解决的是`读性能瓶颈`（单库读上限），当然本身sql语句有问题，读写分离并不能解决什么问题，如果是全同步的话，依然会卡死`写实例`；如果是半同步的话，会导致数据不一致，有缓存的话，简直是灾难；  
公司之前使用的是多主模式，`mgr` \+ `mycat`

1. 线性提升数据库读性能
2. 通过消除读写锁冲突

b）水平切分

1. 线性降低单库数据容量
2. 线性提升数据库写性能

水平切分，一般有两种维度，一种是服务自己去做业务拆分；一种是中间件，例如阿里云的hdb（或者drds）；  
自己拆业务比较彻底一点，但是会复杂一点；中间件要简单一点，但是依赖设计的distribute key（sql语句需要命中，否则会全节点扫描）；