雪球中间件技术分享

目录

对应的PPT资源链接:雪球-基础组件功能使用.key-互联网文档类资源-CSDN下载

zookeeper:

redis

elasticsearch

kafka

MySQL

maven

metric

log

grpc

redis sdk

xueqiu-toolbox

test-tools

xueqiu-parent(不维护)

snowball-common(不维护)

eventcenter(不维护)

datasource(不维护)


zookeeper:

    命名服务:在分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务
    配置管理:分布式环境下,对配置文件修改后,希望能够快速同步到各个节点上。(apollo)
    集群管理:分布式环境中,可根据节点实时状态做出一些调整。
    分布式锁:控制锁的时序。各个客户端在某个Znode下创建临时Znode,这个类型必须为CreateMode.EPHEMERAL_SEQUENTIAL,这样该Znode可掌握全局访问时序。
    分布式队列:当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
Consistency:一致性,Consensus:协同
    我们常说的一致性(Consistency)在分布式系统中指的是对于同一个数据的多个副本,
    其对外表现的数据一致性,如线性一致性、因果一致性、最终一致性等,都是用来描述副本问题中的一致性的。
    而共识(Consensus)则不同,简单来说,共识问题是要经过某种算法使多个节点达成相同状态的一个过程。
    在我看来,一致性强调结果,共识强调过程。
共识模型
    1.主从同步
    2.多数派
Paxos(非集中式)
    Paxos 存在活锁问题:当 第一个 proposer 在第一阶段发出请求,还没来得及后续的第二阶段请求,
    紧接着第二个 proposer 在第一阶段也发出请求,如果这样无穷无尽(如果发现顺序号被新的 proposer 更新了,则引入一个随机的等待的超时时间)
Multi Paxos
    Multi Paxos 引入 Leader,也就是唯一的 proposer,所有的请求都需经过此 Leader。
    因为只有一个节点维护提案编号,这样,就省略了第一轮讨论提议编号的过程。
    Raft 和 ZAB 协议其实基本上和这个一致,两者的差别很小,就是心跳的方向不一样。
Raft
    Raft中每个副本随机选择超时时间(论文中例子为在150-300ms中),降低了冲突发生的可能性。
    当leader成功将该log项复制到超过一半的副本上后,leader认为该log项(及其之前的所有log项)可以被提交了
    各个副本上的log可能会出现各种不一致的情况:Raft用来处理这种情况的对策很简单:以leader上的日志为准,将与leader不一致的日志进行重写
ZAB
  ZAB主要用于构建一个高可用的分布式数据主备系统。
  Paxos算法是用于构建一个分布式的一致性状态机系统。
client:
Chubby中client只允许访问leader进程,但对数据使用了基于Leases的缓存机制,保证了副本的线性化能力
Zookeeper则放松了一致性要求,client不仅可以访问非leader的副本,而且使用“watch”机制提高系统处理能力。
CAP
    从CAP定理看出,一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个需求。对于一个分布式系统,分区容错性是一个最基本的需求。
BASE
    是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

redis

    缓存:如面对数据高并发的读写、海量数据的读写等
    分布式锁:非超时情况下,锁只能由有锁的线程进行释放,可以在value的时间戳中,拼上线程特征码
    消息队列:基于内存级别的高QPS
    限时业务的运用:利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。
    计数器相关问题:一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等
    排行榜相关问题:雪球热股榜、雪球大V排行榜、雪球的各种排行榜
    延时操作:在订单生产后我们占用了库存,10分钟后去检验用户是够真正购买,如果没有购买将该单据设置无效,同时还原库存。
    点赞、好友等相互关系的存储:
        每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。
        利用set存储用户之间的点赞关联的,另外在点赞前判断是否点赞过就利用了sismember方法
协议
    一:TCP协议之上
    二:RESP序列化协议
数据结构与操作
    全局:keys dbsize exists del expire(好几个时间级别) type rename ttl
    数据结构:object encoding 
    String:set|mset key value [ex second] [px milliseconds] [nx|xx]  (编码:int embstr raw)
    hash:hset|hmset key field value [field value ...] (编码:ziplist<512个 64kb hashtable)
    list:rpush key value [value ...]{linsert listkey before key key} (编码:ziplist<512个 64kb linkedlist quicklist)
    set:sadd key element [element ...](编码:inset<512个  linkedlist quicklist)
    zset:zadd key score member [score member ...] (编码:ziplist<512个 64kb skiplist)
sacn:
    在scan过程中出现键的变化(增加删除修改),会出现新增没有遍历到、出现键的重复
mget: 
    https://singgel.blog.csdn.net/article/details/105583251
    jedis会在mget的时候遇见同样的跨分区问题,jedis采用的是循环sleep 10ms查看数据有没有到达,后面作者使用线程池get
    lettuce是按照slot进行mget请求(so,要去改源码?单纯了为了快,我觉得作者是不能接收的 作者的目的是实现mget功能,并不是用pipeline混淆mget概念)
pipeline:
    注意:一次组装pipeline量过大会增加client等待时间,还会造成网络阻塞,分成多个pipeline来完成
    Redis Cluster在设计中没有使用一致性哈希:如果参数的多个key在不同的slot,在不同的主机上,那么必然会出错。因此redis cluster分布式方案是不支持pipeline操作
事物:
    出现语法错误(eg:sett),导致事物无法执行
    运行时错误(eg:sadd–zadd),导致执行,结果无法回滚
    若要确保事物中的key没有被其他客户端修改:watch
Lua:
    script load:将Lua脚本加载到内存
    script exists:判断SHA1是否在
    script flush:清除已经加载的所有Lua脚本
    script kill:杀掉正在执行的Lua脚本
auth:
    redis线上动态增加密码认证
    1.基于proxy代理实现:https://www.dazhuanlan.com/2020/01/20/5e253da29ab4f/
    2.修改jedis源代码:https://blog.csdn.net/TVwR8OfV0P/article/details/80155363
    3.我们使用的是lettuce,基于netty编写的,如果要是修改源码的话,那么后续版本的更新迭代就不好实现了,因此,在使用层面的SDK做了动态的链接转换
客户端输入缓冲区:
    Redis为每一个客户端分配了输入缓冲区,动态规划的,最大为1G,超过后客户端将被关闭
    qbuf:代表客户端缓冲区的总容量
    qbuf-free:代表了客户端的剩余容量
    输入缓冲区不收maxmemory控制
    假设一个Redis实例设置了maxmemory为4G,已经存储了2G数据,如果此时输入缓冲区使用了3G,可能会造成数据丢失、键值淘汰、OOM的情况
    Redis的处理速度跟不上输入缓冲区的输入速度,并且每次输入缓冲区的命令包含了大量的bigkey
    Redis发生阻塞,短期内不能处理命令,造成客户端命令积压在输入缓冲区,造成缓冲区过大
客户端存活状态:
    maxclients:限制最大客户端连接数(默认10000)
    timeout:连接最大空闲时间
    tcp-keepalive进行活性检测,防止大量死连接占用系统资源(默认0)
    tcp-backlog TCP握手后的连接队列(默认511)
客户端常见异常:
    无法从连接池获取到连接: blockWhenExhausted=flase不等待、存在慢查询阻塞了连接池
    客户端读写超时: 读写超时间设置过短、命令本身慢、网络不正常、Redis自身阻塞
    客户端连接超时: 连接超时设置过短、Redis阻塞、网络不正常
    客户端缓冲区异常: 输出缓冲区慢、长时间时间限制连接被主动断开、不正常并发读写
    Lua脚本正在执行: Busy Redis is busy running a script
    Redis正在持久化文件: Loading Redis is loading the dataset in memory
    Redis内存超过maxmemory设置: OOM command not allowed when use memory
    客户端连接数过大: ERR max number of clients reached
    主从内存不一致: 缓冲区的大小占用了大量内存空间
    客户端周期性超时: 运维层面架空慢查询、开发层面加强对Redis理解
穿透优化:
    1.缓存空值
    优点:保护后端数据源 数据压力
    缺点:意味着更多的空间(如果是攻击,问题就严重了)、缓存和存储有一个时间窗口不一致
    2.布隆过滤器拦截
    在访问缓存和存储前,将存在的key用布隆过滤器提前保存起来做第一层拦截
    https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
雪崩优化
    1.保证缓存层服务的高可用性
    2.依赖隔离组件为后端限流并降级
    https://github.com/resilience4j/resilience4j
    3.故障演练,上线前就关闭缓存压测一下
热点key优化
    问题:
        热点key(例如热门的新闻事件)并发量贼大
        重建缓存不能短时间内完成(复杂计算,sql、io、多依赖等)
        在缓存失效期间大量线程重建缓存造成后端压力骤增
    解决:
        减少重建次数、数据尽可能一致、较少的潜在危险
        互斥锁:等上一个把缓存整出来、先看看有没有
        永不过期:缓存方面、功能方面(逻辑过期时间)
寻找热点key
    1.客户端,每次调用redis命令时使用字典记录
    缺点:
    无法预知key个数,存在内存泄漏
    对代码侵入,多个语言维护困难
    只能了解客户端热点key,无法规模化运维
    2.代理端(Twemproxy、Codis)
    3.redis服务端
    使用monitor命令
    Facebook的redis-faina(尽可能在本机执行,存在单点,影响线上性能问题)
    4.机器
    对TCP抓包,采用ELK体系下的packetbeat插件(也可以对MySQL检测)
    处理热点key:
    拆分复杂数据结构
    迁移热点key,将热点slot单独迁到一个节点上
    本地缓存加通知机制(发布订阅解决不一致问题)
预热JedisPool、控制连接数、超时时间
    1.redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out。
    2.redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    3.redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. 
    Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
    https://singgel.blog.csdn.net/article/details/102894984
    https://github.com/Xueqiu-Tech/RedisFullCheck
    https://github.com/xueqiu-tech/RedisShake

elasticsearch

    社区搜索
    基础设施中的日志平台
    行情的选股器
index管理:
    ILM的管理:https://www.elastic.co/guide/en/elasticsearch/reference/current/ilm-index-lifecycle.html
    ◼ 合理切分索引和制定分片大小,避免节点故障后索引恢复速度慢的问题
    ◼ 日志收集场景下写请求远远大于读请求,尽量均匀分配分片在每个节点上,分摊写压力
    ◼ 节点磁盘独立,避免节点间产生IO竞争
    查询和写入的性能与索引的大小是正相关的,所以要保证高性能,一定要限制索引的大小,具体来说是限制分片数量和单个分片的大小;
    每个索引和分片都会产生一定的资源开销:
        分片过小会导致段过小,进而致使开销增加。您要尽量将分片的平均大小控制在至少几 GB 到几十 GB 之间。
        对时序型数据用例而言,分片大小通常介于 20GB 至 40GB 之间。
        确保对于节点上已配置的每个 GB,将分片数量保持在 20 以下。如果某个节点拥有 30GB 的堆内存,那其最多可有 ? 个分片
数据迁移:
    使用elastic-dump备份stock-es生产环境所有业务索引settings mappings和文档数据
性能优化:
    合理的控制index
    https://www.elastic.co/guide/en/elasticsearch/reference/current/size-your-shards.html
    索引设置
        ◦ 索引刷新间隔 index.refresh_interval 为30s或更大
        ◦ 事务日志持久化模式 index.translog.durability 为 async
        ◦ 推迟分片分配 index.unassigned.node_left.delayed_timeout 为 10m
        ◦ 合并线程数 index.merge.scheduler.max_thread_count 对机械硬盘为1
        ◦ 限制节点分片数 index.routing.allocation.total_shards_per_node 一般为1或2
        ◦ 设置合理的refresh时间 index.refresh_interval: 300S 
        ◦ 设置合理的flush间隔 index.translog.flush_threshold_size: 4g index.translog.flush_threshold_ops: 50000 
        ◦ 适当增加索引限制 indices.store.throttle.max_bytes_per_sec: 60mb 
        ◦ 适当提高bulk队列 threadpool.bulk.queue_size: 1000
    lucene merge的困扰
        问题根源:
        1.Lucene 索引同时只能由⼀个线程执⾏写操作
        2.lucene要根据条件进⾏索引段合并(merge),以提⾼查询效率 当如此⼤的数据量⼀旦触发merge滚雪球效应的时候,可能会持续⼏分钟。导致后续发送过来的数据等待写⼊,同 时数据也会挤压在内存,也会导致内存问题。
        优化⽅向:
        1.将⼀次⻓merge合并操作尽量分散在多次merge合并操作中调整lucene merge操作相关参数
        2.将⽇志量⼤的应⽤分布到多个lucene 索引中,同时避免不同⼤应⽤分布到相同 lucene 索引上。
    避免使用 cache
    避免对分词字段排序
    避免deep pagination
    大量拉数据 
    索引太大搜索慢怎么办
        ◦ 选定数据可以分片的维度,然后: 将选定维度设置为routing key,将同一个key的数据route到同一个shard
        ◦ 前提是routing key的基数(cardinality)要高,shard数量要少
        ◦ 否则可能导致数据分布不均,从而产生热点问题
    GC优化:有时可能因为gc时间过长,导致该数据节点被主节点踢出集群的情 况,导致集群出现不健康的状态,为了解决这样的问题,我们适当 的调整ping参数。

kafka

    RabbitMQ:RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP, SMTP, STOMP,也正因如此,它非常重量级,更适合于企业级的开发。
    ZeroMQ:ZeroMQ号称最快的消息队列系统,尤其针对大吞吐量的需求场景。ZMQ能够实现RabbitMQ不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,技术上的复杂度是对这MQ能够应用成功的挑战。
    ActiveMQ:ActiveMQ是Apache下的一个子项目。 类似于ZeroMQ。
partition
    无法弹性扩容:对partition的读写都在partition leader所在的broker,如果该broker压力过大,也无法通过新增broker来解决问题;
Push vs. Pull
    push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。
    pull模式则可以根据Consumer的消费能力以适当的速率消费消息。
producer
    Producer发送消息到broker时,会根据Paritition机制选择将其存储到哪一个Partition。如果Partition机制设置合理,所有消息可以均匀分布到不同的Partition里,这样就实现了负载均衡。
consumer
    Consumer Group
    因为offet由Consumer控制,所以Kafka broker是无状态的,它不需要标记哪些消息被哪些消费过,也不需要通过broker去保证同一个Consumer Group只有一个Consumer能消费某一条消息,因此也就不需要锁机制,这也为Kafka的高吞吐率提供了有力保障。   
    消费者新加入和退出会造成整个消费组rebalance:导致数据重复消费,影响消费速度,增加e2e延迟;
At most once 消息可能会丢,但绝不会重复传输
    可通过设置Producer异步发送实现At most once
At least one 消息绝不会丢,但可能会重复传输
    默认情况下一条消息从Producer到broker是确保了At least once
Exactly once 每条消息肯定会被传输一次且仅传输一次,很多时候这是用户所想要的。
    Producer可以生成一种类似于主键的东西,发生故障时幂等性的重试多次,这样就做到了Exactly once,当出现异常或超时,生产者查询此分区最后一个消息,用于决定后续操作时重传还是继续发送。
    consumer消费者根据主键去重。关闭自动提交 offset 的功能,不使用 Offsets Topic 这个内部 Topic 记录其 offset,而是由消费者自动保存 offset。
spring-kafka
    KafkaTemplate
        消息结果回调:KafkaSendResultHandler 当我们使用KafkaTemplate发送消息成功的时候回调用OnSuccess方法,发送失败则会调用onError方法。
        异步、同步发送消息:同步只需要在send方法后面调用get方法即可。
    ReplyingKafkaTemplate
        使用Headers设置回复主题(Reply_Topic),这种方式比较特别,是一种请求响应模式,使用的是ReplyingKafkaTemplate类
        手动转发,使用@SendTo注解将监听方法返回值转发到Topic中
    KafkaListenerEndpointRegistry
        @KafkaListener这个注解所标注的方法并没有在IOC容器中注册为Bean,而是会被注册在KafkaListenerEndpointRegistry中
        我们需要判断容器是否运行,如果运行则调用resume方法,否则调用start方法
    RecordFilterStrategy
        消息过滤器可以在消息抵达监听容器前被拦截,过滤器根据系统业务逻辑去筛选出需要的数据再交由KafkaListener处理。
数据同步至kafka:
    sqlserver数据实时同步至kafka:https://singgel.blog.csdn.net/article/details/86162990
    canal实现mysql实时数据binlog同步:https://singgel.blog.csdn.net/article/details/86166154
use case:
kafka consumer不消费造成积压排查
    1.KafkaConsumer源码实现了Consumer接口
    .Consumer接口实现了Closeable

MySQL

B-Tree和B+Tree
    MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。
    在B-Tree中按key检索数据的算法非常直观:
    首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。(O(logdN)O(logdN))
    插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质
    B-Tree有许多变种,其中最常见的是B+Tree,例如MySQL就普遍使用B+Tree实现其索引结构
    内节点不存储data,只存储key;叶子节点不存储指针。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入
    非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
    经常看到有帖子或博客讨论主键选择问题,有人建议使用业务无关的自增主键,有人觉得没有必要,完全可以使用如学号或身份证号这种唯一字段作为主键。
数据库锁
    对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);
    表锁开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低;
    行锁开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高;
    悲观锁:select * from xxxx for update
    乐观锁:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version}
    间隙锁GAP:为了防止幻读
    死锁:
    1)以固定的顺序访问表和行。
    2)大事务拆小。
    3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
    4)降低隔离级别。
    5)为表添加合理的索引。
一台MySQL服务器上建立10w个连接的方法
    ulimit -n100000
    在my.cnf文件中加上下面这行设置,然后重启服务
    thread_handling=pool-of-threads(吞吐量相同,但是又95%的响应从3690 ms降到了979 ms)
    一个IP可用端口的最大限制了(65535)
select * from table limit 3000000,10;
    要查询 300w开始后面10条数据;mysql会读取300w加10条这么多的数据,只不过 过滤后返回最后10条而已!!!(5s左右)
    第一种简单粗暴,就是不允许查看这么靠后的数据,比如百度就是这样的
    第二种方法,在查询下一页时把上一页的行id作为参数传递给客户端程序(毫秒级)
    最后第三种方法:延迟关联玄机就处在这个 * 里面,这个表除了id主键肯定还有其他字段  比如 name  age  之类的,因为select  *  所以mysql在沿着id主键走的时候要回行拿数据
select * from table1 where id in ( select uid from table2 );
    mysql in查询的执行过程
    实际上 mysql 内部不是照着我们的想法来运行的,他是从外层执行起走,每扫一行就把id拿来和内层查询比较,所以外层是全表扫描;
    select table1.* from table1 inner join table2 on table1.id=table2.uid;
    mysql先执行了 select uid from table2,然后执行select table1 并且使用了 eq_ref 一对一索引;
SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。
    1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 
    2)ref 指的是使用普通的索引(normal index)。
    3)range 对索引进行范围检索。
    反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫
mysql优化思路
    explain执行计划:使用explain查看,尽量优化sql使rows尽量小,因为查询的时候会对数据行加锁,所以rows越小代表锁的行越少
    mysql 开启慢查询日志
    使用profiler来分析一条query的执行时间和性能瓶颈
    对mysql服务器的优化不要一上来就去优化sql语句,应该首先观察全局情况,至少要先搞清楚
    问题出在哪,应该使用脚本来观察服务器一段时间(一天或更长)的健康状况,比如cpu,io,进程连接数等
    通过 mysqladmin来查看mysql的状态;再 printf的输出重定向到一个文件,这样就可以通过文件数据进行可视化分析;
mysql client配置
    通常是因为连接池大小设置的不合理。如何设置合理的线程池大小需要进行综合考虑。
    这里以sql执行耗时、要支撑的qps为例:
    假设某个接口的sql执行耗时为5ms,要支撑的最大qps为1000。一个sql执行5ms,理想情况下,一个Connection一秒可以执行200个sql。那么理论上我们只需要5个连接即可。
    建议是,比理论值,高3-5倍。
    1 应用启动时,出现获取连接超时异常
        可以通过调大initPoolSize。如果连接池有延迟初始化(lazy init)功能,也要设置为立即初始化,否则,只有第一次请求访问数据库时,才会初始化连接池。这个时候容易出现获取链接超时。
    2 业务高峰期,出现获取连接超时异常
        如果是偶然出现,可以忽略。如果出现的较为频繁,可以考虑调大maxPoolSize和minPoolSize。

maven

maven编译报错: Found Banned Dependency
    原因:
    每个公司都会在CI层做一些jar包的规范,本次的问题出现在的pom规范里,maven-enforcer-plugin插件banned了一些有安全隐患的依赖
    commons-logging和backport-util-concurrent,导致maven打包失败
    解决:
    更改xueqiu-parent的版本为0.5.X,或者手动排除项目中的依赖
maven编译报错:No version are present in the repository
    原因:
    1.在咱们rolling的编译机上面也是有一个本地的.m2仓库,也是需要去nexus私服下载,下载失败导致
    2.依赖的上游没有将对应的版本发布至nexus私服仓库
    解决:
    使用rolling界面的重新触发编译,通知依赖方发布
maven编译报错:Failure to find
    原因:
    1.m2 缓存里有这个 package 的路径,但拉取的pom 文件有个 .lastUpdate 后缀,里面的内容和 artifactory 里不一样,并且极短
    2.nexus私服仓库还没有下载下来
    解决:
    删除.lastUpdated的文件,重新编译,没下载下来的稍等会儿 让SRE解决后重试
maven编译报错:Failed to read artifact descriptor for
    原因:
    需要依赖的pom或者jar在nexus私服仓库没有找到,跟No version are present in the repository是同一个问题
    解决:
    1.在私服仓库搜一下,没有的话让业务方deploy一个
    2.私服仓库有的话,让SRE重新在私服上wget一个

metric

TSDB数据库
    试想一下:Tesla 自动驾驶、华尔街自动交易算法、智能家居、能够实现日内闪电般运抵的交通网络和纽约市警察局发布的开放数据,它们都有哪些共同点?
    这些应用程序均依赖一种衡量事物随时间的变化的数据形式,这里的时间不只是一个度量标准,而是一个坐标的主坐标轴。
    时间序列数据库的特点:
        大部分时间都是写入操作。
        写入操作几乎是顺序添加,大多数时候数据到达后都以时间排序。
        写操作很少写入很久之前的数据,也很少更新数据。大多数情况在数据被采集到数秒或者数分钟后就会被写入数据库。
        删除操作一般为区块删除,选定开始的历史时间并指定后续的区块。很少单独删除某个时间或者分开的随机时间的数据。
        基本数据大,一般超过内存大小。一般选取的只是其一小部分且没有规律,缓存几乎不起任何作用。
        读操作是十分典型的升序或者降序的顺序读。
        高并发的读操作十分常见。
    TSDB项目
        influxDB    https://influxdata.com/
        RRDtool    http://oss.oetiker.ch/rrdtool/
        Graphite    http://graphiteapp.org/
        OpenTSDB    http://opentsdb.net/
        Kdb+    http://kx.com/
        Druid    http://druid.io/
        KairosDB    http://kairosdb.github.io/
        Prometheus    https://prometheus.io/
graphite
    大部分项目通过 [xueqiu-server, xueqiu-common, redis-cluster...]等项目依赖xcommon (也有少量直接依赖)
    xcommon内部再基于io.dropwizard.metrics 自定义Reporter实现 将监控到的数据输出到Kafka(xrealtime-logger, xueqiu-logger-impls)中 SRE再通过消费Kafka获取监控数据 建立索引 在Grafana中绘制监控报表
        该项目基于RMI协议结合Rolling系统提供的JMX信息对容器中存在的服务进行Metrics的爬取
        初期开发原因是因为部分第三方Java服务(例如Kafka、FlumeNG、HBase)无法像现存的监控数据库
        推送监控数据 在解决这个问题的过程中 确定未来的方向是让容器管理者主动去服务中爬取监控信息
Prometheus
    这玩意和skyWalking很像
    Prometheus是由SoundCloud开发的开源监控报警系统和时序列数据库(TSDB)。Prometheus使用Go语言开发,是Google BorgMon监控系统的开源版本。
    Prometheus和Heapster(Heapster是K8S的一个子项目,用于获取集群的性能数据。)相比功能更完善、更全面。Prometheus性能也足够支撑上万台规模的集群。
    Prometheus的特点:
        多维度数据模型。
        灵活的查询语言。
        不依赖分布式存储,单个服务器节点是自主的。
        通过基于HTTP的pull方式采集时序数据。
        可以通过中间网关进行时序列数据推送。
        通过服务发现或者静态配置来发现目标服务对象。
        支持多种多样的图表和界面展示,比如Grafana等。
    Prometheus不适用的场景
        Prometheus它的价值在于可靠性,甚至在很恶劣的环境下,你都可以随时访问它和查看系统服务各种指标的统计信息。 
        如果你对统计数据需要100%的精确,它并不适用,例如:它不适用于实时计费系统。

log

形象的解释下监控、链路追踪、日志这三个平台的意义
    1.日志平台:日志是最能记录案发现场的东西,就好比监控视频、现场人证物证,对各位“警察”分析案发现场至关重要
    2.监控平台:而metric就好比是“电子交通警察”,记录数据指标信息,能够实时的“抓拍违章信息”并发送告警
    3.链路追踪:tracing就像是你在排查问题时候的“警犬”,能够顺着气味帮你串联起来多个“作案地点”,找出背后的“真凶”将其绳之於法
接入实现:
    目前的规划是在rolling的CICD中增加logging的自动管理由服务的接入方控制日志是否采集
    但是由于当前阶段的工程进度和人力不足先在docker的镜像制作阶段修改每个项目的start脚本控制
采集:
    logback的pattern
    appName和instance等基础的信息不再logback里面配置
    原因是:交付给使用方的东西,依赖越少越好,尽可能的在中间层实现(就好比容器的基础参数,会在采集器里面实现)
    filebeat性能优化:
    elk-logstash性能问题:
    elk-logstash时区问题:

grpc

xueqiu-grpc

redis sdk

redis-cluster:
    stock-api redis P99 变慢分析和建议:
        redis 内存有较大抖动,迅速上升然后回落,持续时间和 http 响应慢窗口一致;同时,Connected Client Number 也是迅速上升到4k左右然后回落
        优化建议
        针对客户端,连接池的最佳性能是 maxTotal = maxIdle,这样就避免连接池伸缩带来的性能干扰。实际上最靠谱的值是通过监控来得到“最佳值”
        针对服务端,继续排查和测试 redis server 的性能和需要优化的点;对于已经出现 cpu 瓶颈的实例(stock-quote)进行扩容
    应用无法接收到sentinel消息的问题排查
        应用端建立连接时带了keep-alive,因故断掉连接后,在redis-server和应用端需保持7200s(2小时),雪球设置一般是1200s(20分钟)
redis-cluster4
    lettuce MGET性能:
    lettuce偶现Connection reset by peer异常排查:
    按照lettuce的逻辑,有以下case
        1.网络连接通过防火墙,而防火墙有一定的超时机制,在网络连接长时间不传输数据时,会导致这个tcp连接被关闭
        2.redis服务的链接数达到上限,会将最新的链接给关闭
        3.redis服务宕机、重启
        4.tcp数据长度不一致
        5.没有设置keepalive
        6.timeout时间设置太短
    解决方案就是优化redis.conf的一些配置项:
        timeout
        tcp-keepalive
        tcp-backlog
        maxclients
    默认ForkJoinPool引发的RedisClientP99升高:https://docs.snowballfinance.com/pages/viewpage.action?pageId=104272724

xueqiu-toolbox


    目前该项目指定的默认MySQL SDK版本为8.0.x 实际使用中请按照对应的数据库实例设置版本 推荐版本如下: 
    8.0.x → 8.0.22
    5.1.x → 5.1.49
    5.0.x → 5.0.8
    通过db-pool.properties 指定单个项目的连接池的配置 配置项与druid一致 默认配置存在于_db-pool.properties中
    如果对不同的数据源有特定配置 可以通过{datasourceName}-db-pool.properties指定(datasourceName是ConnectionPool创建时池时指定的)
    建议每个使用者自己设置自己的池配置
    如果要使用读写分离功能 需要引入xueqiu-spring-toolbox项目
    0.0.51增加了mysql client日志:版本升级之后再logback配置下就行,默认打在stdout下
    xueqiu-toolbox-metrics 提供一个简化版的API与io.dropwizard.metrics进行交互 
    之前的监控多是通过自己写Reporter收集监控数据 由于接下来监控信息可能会委托给SRE自行收取 更改成了使用JMX Reporter 另写了实现类通过JMX的API获取监控情况
    使用这个临时解决方案 需要将相关的logback配置信息导入进来:

snowball-common(不维护)

AppInfo配置流程:
    说明:
    AppInfo是一个进行accessLog日志采集的组件类。
    功能:
    采集服务的http接口日志,发送至新创建的KAFKA topic,大数据消费新建的topic将数据落地至hive的logs库下
InheritableThreadLocal线程池中上下文传递的问题
    问题:
    doPost的这个从XDC查询数据异步查询的方法所报的error异常,该方法所采集的traceId和http的url路径不对应,而且这种不对应关系还不是必然发生的,存在一定的关联性且偶然发生
    原因:
    当MDC中的InheritableThreadLocal的map内容被改写后,后面的执行线程如果复用之前的,获取的threadLocal的信息也还是之前的,至此问题分析完毕,清晰明了

eventcenter(不维护)

rabbitMQ服务内存占用大问题:
Command timed out after 1 second(s):

datasource(不维护)

datasource的metric问题:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值