欢迎光临
我们一直在努力

使用Active-Choices-Plugin插件将十个Job合成一个

mumupudding阅读(9)

本文首发于:Jenkins 中文社区

作者:eryajf

现在Spring Cloud越来越火爆,许多公司也都在如火如荼投入使用中,而微服务最大的一个特点,就是多,同一大项目之下,可能会被拆分成十几二十几个子服务,对于运维而言,可能也需要一个对应一个地在Jenkins中创建十几二十几个Job。

刚刚还在一个博主的自我介绍里看到这样一句话:喜欢一切优雅的运维方式···

于是,我一直在想着,通过一些合理的参数变幻,从而将刚刚提到的十几二十几个服务,汇集到一个Job当中就能完成。

1,环境说明

  • 主机:CentOS-7.5
  • Jdk:jdk1.8.0_192
  • Tomcat:8
  • Jenkins:2.177

如上环境的准备工作本文就不赘述了。

2,安装插件。

3,使用前介绍。

插件安装之后,可以在项目配置中的参数化配置中看到一些新增了的选项。

  • 1,Active Choices Parameter(主动选择参数)Active Choices参数使用Groovy脚本或Scriptler目录中的脚本动态生成构建参数的值选项列表。
  • 2,Active Choices Reactive Parameter(主动选择反应参数)根据主动选择参数的选项而提供不同的对应值或者列表选项。
  • 3,Active Choices Reactive Reference Parameter(主动选择反应参考参数)根据主动选择参数的选项而展示对应参数的一些说明,与第二项的区别在于本参数只作为说明信息,而不能够作为变量往下传递。

可能刚刚这些说明都比较抽象,接下来容我通过项目实战,来对其进行一一解析。

4,配置前分析。

优秀的插件,它的优秀之处,往往是需要我们结合生产实际,开动聪明的大脑,打破常规使用套路来成就的。

因此,如何才能更好地应用插件的优秀功能,需要我们先对项目进行分析,从全局的眼光,判断项目前后该配置什么样的参数来进行联动。

这里我说明一下我准备的实验项目情况,为了简便测试,我这里仅使用两个项目来进行举例,聪明的你也一定能够通过案例进行举一反三,将二合一推广为十合一的。

两个项目的情况如下:

1,eureka

2,user

从刚刚这些配置里边可以看出,有不少共同点,也有不少不同点,我们只需把眼光放在不同点上,通过一些统一的变量控制,就能达到二合一的目的。

另外说明一点,这个项目已经部署在k8s环境当中,因此我的脚本内容也就展示成了k8s项目部署的流程了。

5,创建项目。

首先创建一个自由风格的Jenkins项目,然后配置一下项目构建保存历史。

6,字符参数。

添加一个名为branch的字符参数以用于控制代码分支的变量。

7,选项参数。

添加一个名为mode的选项参数以用于控制部署或者回滚的变量。

8,选择参数。

1,主动选择参数

首先添加一个主动选择参数,用于控制项目名称这个变量。

  • Name:project
  • Groovy Script:
return["eureka","user"]
  • Description:选择对应的应用名称部署对应的应用。
  • Choice Type:Radio Buttons

2,主动选择反应参数

接着添加一个主动选择反应参数,用于控制项目类型这个变量。

Name:type

  • Groovy Script:
A=["server"]B=["service"]if(project.equals("eureka")){return A} else if(project.equals("user")){return B}
  • Description:跟随项目的选择自动弹出对应类型
  • Choice Type:Single Select
  • Referenced parameters:project

3,主动选择反应参数

然后再添加一个主动选择反应参数,用于控制项目端口这个变量。

  • Name:port
  • Groovy Script:
if(project.equals("eureka")){return ["8761"]} else if (project.equals("user")){return ["6666"]}
  • Description:跟随跟随项目的选择自动弹出对应端口
  • Choice Type:Single Select
  • Referenced parameters:project

这样,对应的参数都创建完毕了,大概有以下几个小的注意点需要注意:

  • 1,参数的名称将是整个构建流程使用的一个变量,因此起名的时候需要注意。
  • 2,创建了一个主动选择参数,和两个主动选择反应参数,是因为我们的实际需求需要两个真实有效的参数,如果最后的port项选择了主动选择反应参考参数,那么到后边是无法显式使用的。
  • 3,注意后两个跟随参数中的Referenced parameters,都需要填写主动参数的名称,才能够前后贯通,实现联动。

9,Git地址配置。

接着就该添加Git地址了,同样,这个地方也应该合理利用项目标准化的优势,合理应用变量来进行配置。

具体如下图所示:

10,执行脚本。

接下来就该通过脚本来完成构建的主要流程了。

#!/bin/bashsource /etc/profile###set color##echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; }echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; }echoYellow() { echo $'\e[0;33m'"$1"$'\e[0m'; }##set color###version=`date +%Y%m%d%H%M%S`echo -------------------------------------# 克隆项目并编译echoGreen "开始进行mvn编译!"cd  $WORKSPACE && mvn clean install -DskipTests=true[ $? != 0 ] && echoRed "请注意,在执行mvn编译时出错,故而退出构建,需开发同学自检代码!" && exit 1cd  $WORKSPACE/target/ && mv ishangjie-$project-$type-1.0.0.jar app.jar# 创建docker镜像cat > run.sh << EOF#!/bin/bashsource /etc/profilejava -jar /opt/app.jar --spring.profiles.active=test1EOFchmod +x run.shcat >Dockerfile << EOFFROM 192.168.10.1/public/jdk:1.8MAINTAINER eryajf <liqilong@edspay.com>ENV LANG en_US.UTF-8ADD   app.jar /opt/app.jarADD   run.sh  /EXPOSE $portENTRYPOINT [ "sh", "-c", "/run.sh" ]EOF# 构建镜像echoGreen "开始构建当次镜像!"docker build -t 192.168.10.1/isj/$project:$version .[ $? != 0 ] && echoRed "请注意,在执行镜像构建时出错,故而退出构建,请联系运维同学处理!" && exit 1# 上传到docker私服echoGreen "开始将镜像push到私服!"docker push 192.168.10.1/isj/$project:$version[ $? != 0 ] && echoRed "请注意,在执行镜像上传时出错,故而退出构建,请联系运维同学处理!" && exit 1docker rmi 192.168.10.1/isj/$project:$version#更新镜像echoGreen "开始将最新镜像部署到远端!"rancher kubectl set image deployment/isj-$project isj-$project=192.168.10.1/isj/$project:$version -n isj-wfw[ $? != 0 ] && echoRed "请注意,在执行镜像更新时出错,故而退出构建,请联系运维同学处理!" && exit 1echoGreen "部署完成!"

针对这个脚本有几点简单说明:

  • 1,因为应用到了颜色输出,因此记得在构建环境当中开启color颜色输出。
  • 2,尽量在关键地方添加一下判断,然后输出明了的内容以提高生产效率,比如编译有问题,直接退出构建,输出开发自检,如果是后边构建问题,同样退出构建,输出联系运维解决。
  • 3,巧用cat的EOF特性,从而也可以将不同的变量控制进来。
  • 4,尽量将所有构建过程的内容都写到Jenkins这里来,以便于后期问题排查与分析。
  • 5,因为这是实验,因此没有添加回滚功能,如果添加的话,就针对mode参数做一个判断即可。

11,构建后操作。

因为是多个项目在同一个WORKSPACE下工作,因此,为了避免出现不可预知问题,这里添加了构建后清空WORKSPACE的选项。

12,效果展示。

一切配置完成之后,就可以尝试一下点击构建了。

好了,这就是本期的分享,当然,关于这个插件,这里也只是介绍了其中一种一个思路,可能还有很多种其他的方案,期待各位发挥思维发掘更多妙用。

高性能MySQL06-查询优化(慢查询)

mumupudding阅读(6)

一、分析原因

SQL语句慢查询的原因有多种,如:
1)数据方面:
需要查询的表数据量太大导致性能下降;
是否向数据库请求了不需要的数据行或数据列;
MySQL是否在扫描额外的记录

2)SQL语句太过于冗余

3)等

下面我们列出一下分析SQL查询慢的一些方法:

1、记录慢查询日志

分析查询日志,不要直接打开慢查询日志进行分析,这样比较浪费时间和精力,可以使用pt-query-digest工具进行分析。

2、使用show profile
使用步骤:

1)开启,服务器上执行的所有语句会检测消耗的时间,存到临时表中
set profiling = 1;
2)进行需要分析的SQL查询
...
3)show profiles
4)show profile for query 临时表id

如:

mysql> set profiling = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> show profiles;
Empty set (0.00 sec)

mysql> select * from a;
+------+--------+
| id   | name   |
+------+--------+
|    1 | nosee  |
|    2 | chan   |
|    3 | cheese |
|    4 | xyz    |
+------+--------+
4 rows in set (0.00 sec)

mysql> show profiles;
+----------+------------+-----------------+
| Query_ID | Duration   | Query           |
+----------+------------+-----------------+
|        1 | 0.00054875 | select * from a |
+----------+------------+-----------------+
1 row in set (0.00 sec)

mysql> show profile for query 1;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000063 |
| checking permissions | 0.000013 |
| Opening tables       | 0.000050 |
| System lock          | 0.000018 |
| init                 | 0.000023 |
| optimizing           | 0.000007 |
| statistics           | 0.000018 |
| preparing            | 0.000010 |
| executing            | 0.000004 |
| Sending data         | 0.000104 |
| end                  | 0.000006 |
| query end            | 0.000004 |
| closing tables       | 0.000008 |
| freeing items        | 0.000196 |
| logging slow query   | 0.000022 |
| cleaning up          | 0.000006 |
+----------------------+----------+
16 rows in set (0.00 sec)

3、使用show status

show status会返回一些计数器,show global status 查看服务器级别的所有计数。有时根据这些计数,可以猜测出哪些操作代价较高或消耗时间多。

4、使用explain

explain命令用于分析单条SQL语句,是查看优化器如何决定执行查询的主要方法。

如:

mysql> explain select id from a where id =3\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: id
          key: id
      key_len: 5
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

关于EXPLAIN更详细的内容请查看下一篇文章。

二、查询执行原理

当希望MySQL能够以更高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的。

当我们向MySQL发送一个请求时,MySQL到底做了些什么,下面我们通过一个简单的图解来进行分析:

高性能MySQL06-查询优化(慢查询)

1)客户端发送一条查询给服务器。
2)服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果,否则进入下一阶段。
3)服务器进行SQL解析、预处理,再由优化器生成对应的执行计划。
4)MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。
5)将结果返回给客户端。

上面的每一步都比想象的要复杂,这里将不深入讨论。

三、优化查询过程中的数据访问

1、定位问题

访问数据太多导致查询性能下降。

确定应用程序是否在检索大量超过需要的数据,可能是太多行或列。确认MySQL服务器是否在分析大量不必要的数据行。

2、避免使用如下SQL语句

1)查询不需要的数据,使用limit解决
2)多表关联返回全部列,指定如A.id, A.name, B.age
3)总是取出全部列,SELECT *会让优化器无法完成索引覆盖扫描的优化
4)重复查询相同的数据,可以缓存数据,下次直接读取缓存

3、是否在扫描额外的记录

使用explain来进行分析,如果发现查询需要扫描大量的数据但只返回少数的行,可以通过如下技巧去优化:
1)使用索引覆盖扫描,把所有用的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果
2)改变数据库和表的结构,修改数据表范式
3)重写SQL语句,让优化器可以以更优的方式执行查询

四、优化长难的查询语句

MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多。使用尽可能少的查询是最好的,但有时将一个大的查询分解为多个小的查询也是很有必要的。

1、切分查询

将一个大的查询分为多个小的相同的查询。如一次性删除1000万的数据,要比一次删除1万暂停一会的方案更加损耗服务器开销。

2、分解关联查询

1)可以将一条关联语句分解成多条SQL来执行
2)执行单个查询可以减少锁的竞争
3)在应用层做关联可以更容易对数据库进行拆分

五、优化特定类型的查询语句

1、优化count()查询

1)count(*)中的*会忽略所有的列,直接统计所有列数,因此不要使用count(列名)。MyISAM中,没有任何WHERE条件的count(*)非常快。
2)可以使用explain查询近似值,用近似值替代count(*)
3)增加汇总表
4)使用缓存

2、优化关联查询

1)确定ON或USING子句的列上有索引
2)确保GROUP BY和ORDER BY中只有一个表中的列,这样MySQL才有可能使用索引

3、优化子查询

尽可能使用关联查询来替代

4、优化GROUP BY和DISTINCY

1)这两种查询均可使用索引来优化,是最有效的优化方法
2)关联查询中,使用标识列进行分组的效率会更高
3)如果不需要ORDER BY,进行GROUP BY时使用ORDER BY NULL,MySQL不会进行文件排序
4)WITH ROLLUP超级聚合,可以挪到应用程序处理

5、优化LIMIT分页

LIMIT偏移量大的时候,查询效率较低。可以记录上次查询的最大ID,下次查询时直接根据该ID来查询。

6、优化UNION查询

UNION ALL的效率高于UNION。

经典实例

1、请简述项目中优化SQL语句执行效率的方法,从哪些方面,SQL语句性能如何分析?

考官考点:
1)查找分析查询速度慢的原因
2)优化查询过程中的数据访问
3)优化长难的查询语句
4)优化特定类型的查询语句

对于此类问题,先说明如何定位低效率SQL语句,然后根据SQL语句可能低效的原因做排查,先从索引着手,如果索引没问题,考虑以上几个方面:数据访问的问题、长难查询句的问题、还是一些特定类型优化的问题,逐步排除。

觉得不错请点赞支持,欢迎留言或进我的个人群855801563领取【架构资料专题目合集90期】、【BATJTMD大厂JAVA面试真题1000+】,本群专用于学习交流技术、分享面试机会,拒绝广告,我也会在群内不定期答题、探讨

 

2019年互联网架构设计:高性能的后端

mumupudding阅读(7)

先简略回顾一下。对于互联网产品的高性能架构设计通常包括以下几个大方面:

  1. Web浏览器高性能设计
  2. App客户端高性能设计
  3. 高性能的网络和硬件
  4. 后台服务高性能设计

2019年互联网架构设计:高性能的后端

后端服务一般指用户直接看到的远程服务,涉及到网络硬件、逻辑计算、通信协议和数据存储等部分。下面我们将着重介绍高性能后台服务的设计方法和策略。

一、高性能的网络和硬件

  1. 网络硬件是提供实现高性能服务的先决条件,如果网络硬件失败,再优秀的团队也是“巧妇难为无米之炊”。互联网产品在网络硬件方面经常需要使用的高性能方案有如下几种:
  2. CDN加速技术。CDN加速将网站的内容缓存在网络边缘(离用户接入网络最近的地方),然后在用户访问网站内容的时候,通过调度系统将用户的请求路由或者引导到离用户接入网络最近或者访问效果最佳的缓存服务器上,由该缓存服务器为用户提供内容服务;相对于直接访问源站,这种方式缩短了用户和内容之间的网络距离,从而达到加速的效果。
  3. 足够的带宽。带宽应该满足在网站峰值的情况还能足够快速的使用,所以网络带宽应该大于峰值流量 = 峰值QPS * 平均请求大小。只有在保证带宽的情况才能实现高性能服务。
  4. 服务器性能。服务器性能主要从CPU、内存和磁盘三个方面来考虑,CPU核心数量能尽量多点,内存大小最好大一点,利用到磁盘存储的话SSD会优于机械磁盘。
  5. 硬件负载均衡设备。对于有条件的团队可以采购硬件负载均衡设备,加强后台服务负载均衡的能力,比如F5。

二、后台服务高性能设计

后台服务的高性能设计是互联网产品高性能架构设计中最重要的一环,对服务整体性能起到决定性的作用。我们来看看设计高性能后台服务的方法:

1分布式缓存。

缓存的本质是通过key-value形式的Hash表提升读写速度,一般情况是O(1)的读写速度。读写量比较高,变化量不大的数据比较适合使用缓存。业内比较成熟的分布式缓存系统有redis/memcache。在此我向大家推荐一个架构学习交流圈。交流学习企鹅圈号:948368769 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

一般的缓存设计架构如下:用户第一次请求应用程序时,通过存储服务直接读取数据,然后将数据存储到缓存系统去,用户第二次请求的时候就直接从缓存系统读取,从而提升读取速度。

2019年互联网架构设计:高性能的后端

对于分布式缓存系统可以Set化部署,比如商品数据缓存到Set1,用户数据缓存到Set2,或者一类用户的数据缓存到Set1,另一类的用户缓存到Set2,如下图:

 

2019年互联网架构设计:高性能的后端

此外,也可以按集群化部署,每一个缓存服务存储的数据都是对等的,可以对外提供同等的服务,所以外部请求需要负载均衡到不同有缓存服务器,如下图:

 

2019年互联网架构设计:高性能的后端

Set化部署的目的主要在于将不同类型的数据路由到不同的地方,好处就是可以减少不同业务数据的耦合,可以针对不同业务进行不同的优化,从而提升整体性能。集群式部署的目的在于,提高缓存系统的对外服务能力,上层业务的路由策略简单灵活,扩缩容比较容易。

2.服务分层

在经典的三层(接入层、逻辑层和存储层)后台服务架构中,三层的划分的原则就是同层次的系统专注处理自己的事情。接入层专注于处理前端和后台服务的接入连通、安全认证和数据转发。逻辑层专注于处理不同业务的无状态逻辑服务。存储层专注于处理业务数据的存储。这样分层的好处在于各个层次能够依据业务特点专注于自己的事情,提高系统复用性,降低业务间的耦合性。在中小型网站中三层架构的典型实现是Nginx(接入层)、Apache Web(逻辑层)、Mysql/Redis(存储层)。

2019年互联网架构设计:高性能的后端

3.操作异步化

目前大型系统中普遍消息队列来将调用异步化,不仅可以提升系统性能还可以提升系统的扩展性。对于大量的数据库写请求,对于数据库的压力很大,同时也会造成数据库响应不及时。可以考虑使用消息队列,数据库的写请求可以直接写入到消息队列,然后通过多线程或多进程从消息队列中读取数据慢慢写入到数据库。消息队列服务器的处理速度会远远快于数据库,所以用户在写入操作时会感觉到很快的写入速度。

此外,消息队列对于请求不均衡的系统,还具有削峰填谷的作用,将短时间内的高峰请求,逐步平摊到更长的时间里去,从而避免短时间内大量请求压跨系统。

 

2019年互联网架构设计:高性能的后端

4.服务拆分

服务拆分有多种说法,比如大系统小做,分布式拆分,分层结构以及目前很流行的微服务化。不过服务拆分一般来说有以下原则:

  • a.高内聚、低耦合: 将耦合性低的业务逻辑划分为不同系统,将聚合性高的业务逻辑划分为同一个系统。
  • b.单一职责原则:对于一个层次或者一个模块应该保持相对单一的职责,专注于自己的服务。
  • c.故障隔离:不同系统必须相对独立设计和运行,能够独立处理自己的故障,而不至于影响全局。
  • d.独立运维和持续交互:对于不同的系统可以随时迭代更新,而不至于影响其他服务。

对于服务拆分主要有纵向拆分和水平拆分两种方法。三层架构就是典型的纵向拆分模式,第2)点有所阐述。对于不同的业务模块,针对业务逻辑和存储服务可以按水平拆分的方法将拆分为不同的系统。比如商品系统逻辑层、订单系统逻辑层、商品系统存储层、订单系统存储层。在此我向大家推荐一个架构学习交流圈。交流学习企鹅圈号:948368769 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

5分布式集群化

分布式集群化是指将不同的业务用集群化的方式部署到不同的机器上去,对于每一个业务都具备大规模集群化的能力,从而提升系统的扩展性和高性能。

 

2019年互联网架构设计:高性能的后端

对于无状态化的被调服务A,在基于负载均衡的技术下,可以通过集群化部署成倍的提升服务性能,比如A1服务的性能是1万请求每秒,那么部署3台A服务机器,那么A服务的性能就是3万请求每秒了。

6.代码优化

对于IO操作的请求可以采用基于状态机的异步化编程。如下图的请求需要业务系统调用三个接口才能返回响应数据,当业务系统收到请求时,将给该请求分配一个seqid,该seqid在接口响应中也应该原封返回,然后并发三个IO接口的请求包,将该seqid对应的请求上下文保存到timer中,然后去并发处理其他请求,从而极大的提升系统性能。

 

2019年互联网架构设计:高性能的后端

此外,高性能的编程模型还有多线程模型、多进程模型、多协程模型和事件驱动模型。

对于数据结构的设计可以采用高效的数据结构,比如典型的key-value缓存系统就是基于hash的基本原理来实现的,hash表的查询效率是O(1),效率极快。

如何打造VUCA时代的敏捷型组织?

mumupudding阅读(5)

e3e90777aae5c0ac7693da9292213437.jpg

王明兰 –原华为、微软创新与转型教练、华为云SaaS产品总监,著名精益&敏捷转型专家

VUCA最早来源于冷战时期,在现代世界意指商业世界越来越不确定性,越来越易变,越来越不可预测,我们已经进入到了VUCA时代。

屏幕快照 2019-06-25 上午10.22.38.png

我们再也不能用原来的那种传统的、计划驱动的方式来工作,因为时代的不确定性,所以大家要拥抱变化。敏捷本身也是在那样一个时代背景下,越来越发展壮大。如果倒回来很多年之前,敏捷不会发展壮大,因为我们还没有进入这样的时代。

以前,企业的生存周期比较长,但是现在企业的生存周期越来越短,从前的几十年、上百年,现在是十年,十五年。不确定性越发严重,所以企业不断的探索怎么能够让自己更加适应市场的变化,适应这个时代,这是很多企业都在尝试敏捷转型背后的驱动力。

屏幕快照 2019-06-25 上午10.26.57.png

这本书大家知道是什么吗? TEAM OF TEAMS ,这本书是很火的畅销书,叫 赋能

在我们看来这本书的内容和名字,实际上讲的就是大规模组织里面怎么能够提升组织的适应能力。赋能讲的大家都知道,在伊拉克战争期间,美国军队怎么改变自己来应对恐怖组织的袭击。我总结了整个书里面介绍了美国军队做了哪几项改变:

1-从集中化军事集团变为小规模特种部队作战单元;2-高度可视化的一些战形迅速做出科学决策;3-持续的反馈和调整

这三点实际上 不仅适用于战争 ,这本书 成为畅销书并不是因为描写战争 ,而是 他在商业领域非常通用

我在很多公司做过敏捷咨询,发现不同的企业里对敏捷转型的迫切性是不一样的,有的企业非常迫切,有的企业还是属于无所谓,有的企业觉得想,但是又不敢。

屏幕快照 2019-06-25 上午10.43.12.png

什么样的企业会更加迫切呢? 有这样一个调查,如果企业的经济环境越来越不确定,这种企业它对转型就更加迫切。比如通讯业:像诺基亚、诺基亚西门子这种通信行业的企业做敏捷非常早,华为虽然不怎么讲,但是在中国的民企里华为做敏捷是最早的一个,华为从08年就在做,默默无闻,因为华为不鼓励员工出来说。

此外,银行业里基本所有的国有大行都在尝试敏捷转型。为什么要转型呢?也是一样地面临业务的压力。

那到底什么样的组织才能称为一个敏捷型组织呢?

屏幕快照 2019-06-25 上午10.46.59.png

我总结了敏捷型组织需要三力:

第一个快速的响应力;第二个强大的执行力;第三个是需要持续的创新力。

光说三力挺容易的事情,同时具备三力,具备极致的情况下的企业非常非常少。

屏幕快照 2019-06-25 上午10.56.13.png

快速的响应力

快速的响应力需要一家企业它的组织结构需要能够快速响应。

什么样的组织机构需要快速响应?如果做一件事情都要层层去审批汇报,然后到最顶层就是审批下来才能往下下达,这种企业,它的响应力一定不会快。

敏捷型的组织是什么样的?敏捷型组织倡导的是端到端的价值链的打通。 比如我我在一些银行企业里做咨询,银行里的研发团队想在手机银行里面上线一个小功能,这本身一个很简单的事情,一般来说一个团队就搞定的事情,但是他们不一样,上线一个功能需要拉通七、八个团队,七、八个团队里又跨越了三、四个部门,上线这个功能,需要两个月的时间。

如果对于一个创业公司来说,非常快就上线了,但是在银行这种组织里就做不到。做不到并不是因为人的能力差,也不是因为技术差,就是因为 组织结构太复杂。做任何一件事情,都需要跨越其他的团队,甚至是其他的部门去协同才能完成。敏捷组织需要的是把这些组织结构打通,构建为全功能的团队,让这些团队能够向一个创业型团队一样具备高速的响应力。

因此, 建设敏捷型组织的结构,就是要把组织结构扁平化,打破壁垒,让每个团队都像创业团队一样

屏幕快照 2019-06-25 上午11.01.23.png

Scrum团队本质上就是最小的敏捷型组织单元 ,Scrum团队每个成员他们构成一个团队之后,就不再依赖于其他团队,就能够交付一个端到端的,这是Scrum的本质之一。

屏幕快照 2019-06-25 上午11.01.47.png

Scrum的发明人 Jeff Sutherland 在这两年新出了一本书叫  《敏捷革命》  ,这本书出的意义就是它描述了Scrum在IT以外的行业里的应用,这点是蛮有突破性的。敏捷虽然发展这么多年,大家以为只是在软件开发领域,没有想过在软件开发以外还可以应用。但是Jeff Sutherland在这本书里介绍了他将Scrum应用在各种各样的行业,包括联合国、政府、电视台等等没有任何软件开发的工作里。

Scrum的本质并不是和软件开发相关的,它的本质前面列的《赋能》里的三点一样:

1-跨职能的团队 2-本质是小循环3-早失败

acb50083-6c2d-4c65-8454-7a6e3bd53a22.png

强大的执行力

执行力这个话题已经谈了很多年了,历史上敏捷跟执行力没什么关系,敏捷不要什么命令控制,好像对执行力感觉是自上而下的。

我之前也是这样想的,也是这样理解的,但是这几年我一些企业的研发以外的部门做咨询,发现这些部门的领导是非常希望有方法帮助他们来去构建执行力的,但是他们没有这样一个有效的方法。我发现敏捷其实是一个非常好的方法帮助他们打造执行力。

屏幕快照 2019-06-25 上午11.54.34.png

这个畅销书叫 《执行》 ,虽然没有提到任何敏捷的理念,但是里面讲的执行的方法,都很有敏捷的思想,这点是蛮让我吃惊的。比如讲的领导需要具备的七条基本行为:

第一、条了解企业和员工第二、坚持以事实为基础第三、确立明确目标和实现目标的先后顺序第四、跟进第五、对执行进行奖励第六、提高员工能力和素质第七、了解你自己

屏幕快照 2019-06-25 下午1.48.40.png

持续的创新力

屏幕快照 2019-06-25 下午1.51.06.png

从组织来说,最核心的就是最大的动力就是危机感,因为一个组织如果没有危机感就没有动力去创新,华为为什么有持续的创新的动力?因为它太有危机感了。

光有危机感还不够,企业需要一套机制融合了自上向下和自下而上来构建持续的创新力,OKR正是提供了这套机制

屏幕快照 2019-06-25 下午1.52.41.png

OKR机制让组织从体制上拥抱创新机制,自下向上。

我觉得OKR整套体现了敏捷的原则,它相当于是敏捷原则在HR领域里面探索出了一套方法,但是又跟敏捷这个词又没有什么关系,这是很奇怪的。因为它允许组织它可以确定除了设定不确定目标之外,还可以自上而下反馈自己的目标。

屏幕快照 2019-06-25 下午1.54.05.png

最后总结,敏捷组织需要构建的三力是:快速的响应力,强大的执行力,持续的创新力。光有响应力没有执行力,这样组织就会想做的很多但是没有办法落地执行;如果没有持续的创新力,这个组织就会容易被颠覆。

文章来源:Worktile敏捷博客

欢迎访问交流更多关于技术及协作的问题。

文章转载请注明出处。

从MySQL源码看其网络IO模型

mumupudding阅读(6)


从MySQL源码看其网络IO模型

前言

MySQL是当今最流行的开源数据库,阅读其源码是一件大有裨益的事情(虽然其代码感觉比较凌乱)。而笔者阅读一个Server源码的习惯就是先从其网络IO模型看起。于是,便有了本篇博客。

MySQL启动Socket监听

看源码,首先就需要找到其入口点,mysqld的入口点为mysqld_main,跳过了各种配置文件的加载之后,我们来到了network_init初始化网络环节,如下图所示:

下面是其调用栈:

mysqld_main (MySQL Server Entry Point) |-network_init (初始化网络)  /* 建立tcp套接字 */  |-create_socket (AF_INET)  |-mysql_socket_bind (AF_INET)  |-mysql_socket_listen (AF_INET)  /* 建立UNIX套接字*/  |-mysql_socket_socket (AF_UNIX)  |-mysql_socket_bind (AF_UNIX)  |-mysql_socket_listen (AF_UNIX)

值得注意的是,在tcp socket的初始化过程中,考虑到了ipv4/v6的两种情况:

// 首先创建ipv4连接ip_sock= create_socket(ai, AF_INET, &a);// 如果无法创建ipv4连接,则尝试创建ipv6连接if(mysql_socket_getfd(ip_sock) == INVALID_SOCKET)  ip_sock= create_socket(ai, AF_INET6, &a);

如果我们以很快的速度stop/start mysql,会出现上一个mysql的listen port没有被release导致无法当前mysql的socket无法bind的情况,在此种情况下mysql会循环等待,其每次等待时间为当前重试次数retry * retry/3 +1秒,一直到设置的–port-open-timeout(默认为0)为止,如下图所示:

MySQL新建连接处理循环

通过handle_connections_sockets处理MySQL的新建连接循环,根据操作系统的配置通过poll/select处理循环(非epoll,这样可移植性较高,且mysql瓶颈不在网络上)。
MySQL通过线程池的模式处理连接(一个连接对应一个线程,连接关闭后将线程归还到池中),如下图所示:
对应的调用栈如下所示:

handle_connections_sockets |->poll/select |->new_sock=mysql_socket_accept(...sock...) /*从listen socket中获取新连接*/ |->new THD 连接线程上下文 /* 如果获取不到足够内存,则shutdown new_sock*/ |->mysql_socket_getfd(sock) 从socket中获取  /** 设置为NONBLOCK和环境有关 **/ |->fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK); |->mysql_socket_vio_new  |->vio_init (VIO_TYPE_TCPIP)   |->(vio->write = vio_write)   /* 默认用的是vio_read */   |->(vio->read=(flags & VIO_BUFFERED_READ) ?vio_read_buff :vio_read;)   |->(vio->viokeepalive = vio_keepalive) /*tcp层面的keepalive*/   |->..... |->mysql_net_init  |->设置超时时间,最大packet等参数 |->create_new_thread(thd) /* 实际是从线程池拿,不够再新建pthread线程 */  |->最大连接数限制  |->create_thread_to_handle_connection   |->首先看下线程池是否有空闲线程    |->mysql_cond_signal(&COND_thread_cache) /* 有则发送信号 */   /** 这边的hanlde_one_connection是mysql连接的主要处理函数 */   |->mysql_thread_create(...handle_one_connection...)   

MySQL的VIO

如上图代码中,每新建一个连接,都随之新建一个vio(mysql_socket_vio_new->vio_init),在vio_init的过程中,初始化了一堆回掉函数,如下图所示:
我们关注点在vio_read和vio_write上,如上面代码所示,在笔者所处机器的环境下将MySQL连接的socket设置成了非阻塞模式(O_NONBLOCK)模式。所以在vio的代码里面采用了nonblock代码的编写模式,如下面源码所示:

vio_read

size_t vio_read(Vio *vio, uchar *buf, size_t size){  while ((ret= mysql_socket_recv(vio->mysql_socket, (SOCKBUF_T *)buf, size, flags)) == -1)  {    ......    // 如果上面获取的数据为空,则通过select的方式去获取读取事件,并设置超时timeout时间    if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_READ)))        break;  }}

即通过while循环去读取socket中的数据,如果读取为空,则通过vio_socket_io_wait去等待(借助于select的超时机制),其源码如下所示:

vio_socket_io_wait |->vio_io_wait  |-> (ret= select(fd + 1, &readfds, &writefds, &exceptfds,               (timeout >= 0) ? &tm : NULL))

笔者在jdk源码中看到java的connection time out也是通过这,select(…wait_time)的方式去实现连接超时的。
由上述源码可以看出,这个mysql的read_timeout是针对每次socket recv(而不是整个packet的),所以可能出现超过read_timeout MySQL仍旧不会报错的情况,如下图所示:

vio_write

vio_write实现模式和vio_read一致,也是通过select来实现超时时间的判定,如下面源码所示:

size_t vio_write(Vio *vio, const uchar* buf, size_t size){  while ((ret= mysql_socket_send(vio->mysql_socket, (SOCKBUF_T *)buf, size, flags)) == -1)  {    int error= socket_errno;    /* The operation would block? */    // 处理EAGAIN和EWOULDBLOCK返回,NON_BLOCK模式都必须处理    if (error != SOCKET_EAGAIN && error != SOCKET_EWOULDBLOCK)      break;    /* Wait for the output buffer to become writable.*/    if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_WRITE)))      break;  }}

MySQL的连接处理线程

从上面的代码:

mysql_thread_create(...handle_one_connection...)

可以发现,MySQL每个线程的处理函数为handle_one_connection,其过程如下图所示:


代码如下所示:

for(;;){ // 这边做了连接的handshake和auth的工作 rc= thd_prepare_connection(thd); // 和通常的线程处理一样,一个无限循环获取连接请求 while(thd_is_connection_alive(thd)) {  if(do_command(thd))   break; } // 出循环之后,连接已经被clientdu端关闭或者出现异常 // 这边做了连接的销毁动作 end_connection(thd);end_thread: ... // 这边调用end_thread做清理动作,并将当前线程返还给线程池重用 // end_thread对应为one_thread_per_connection_end if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))  return;  ... // 这边current_thd是个宏定义,其实是current_thd(); // 主要是从线程上下文中获取新塞进去的thd // my_pthread_getspecific_ptr(THD*,THR_THD); thd= current_thd; ...}

mysql的每个woker线程通过无限循环去处理请求。

线程的归还过程

MySQL通过调用one_thread_per_connection_end(即上面的end_thread)去归还连接。

MYSQL_CALLBACK_ELSE(...end_thread) one_thread_per_connection_end  |->thd->release_resources()  |->......  |->block_until_new_connection

线程在新连接尚未到来之前,等待在信号量上(下面代码是C/C++ mutex condition的标准使用模式):

static bool block_until_new_connection(){  mysql_mutex_lock(&LOCK_thread_count); ......    while (!abort_loop && !wake_pthread && !kill_blocked_pthreads_flag)      mysql_cond_wait(&x1, &LOCK_thread_count);   ......   // 从等待列表中获取需要处理的THD   thd= waiting_thd_list->front();   waiting_thd_list->pop_front();   ......   // 将thd放入到当前线程上下文中   // my_pthread_setspecific_ptr(THR_THD,  this)       thd->store_globals();   ......   mysql_mutex_unlock(&LOCK_thread_count);   .....}

整个过程如下图所示:


由于MySQL的调用栈比较深,所以将thd放入线程上下文中能够有效的在调用栈中减少传递参数的数量。

总结

MySQL的网络IO模型采用了经典的线程池技术,虽然性能上不及reactor模型,但好在其瓶颈并不在网络IO上,采用这种方法无疑可以节省大量的精力去专注于处理sql等其它方面的优化。

SpringCloud微服务架构升级总结

mumupudding阅读(4)


一、背景

1.1 应用系统的架构历史

1.2 什么是微服务?

起源:微服务的概念源于 2014 年 3 月 Martin Fowler 所写的一篇文章“Microservices”。文中内容提到:微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。

通信方式:每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API)。

微服务的常规定义:微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务。

把原来的一个完整的进程服务,拆分成两个或两个以上的进程服务,且互相之间存在调用关系,与原先单一的进程服务相比,就是“微服务”。(微服务是一个比较级的概念,而不是单一的概念)

1.3 微服务架构的优势

  • 可扩展性:在增加业务功能时,单一应用架构需要在原先架构的代码基础上做比较大的调整,而微服务架构只需要增加新的微服务节点,并调整与之有关联的微服务节点即可。在增加业务响应能力时,单一架构需要进行整体扩容,而微服务架构仅需要扩容响应能力不足的微服务节点。
  • 容错性:在系统发生故障时,单一应用架构需要进行整个系统的修复,涉及到代码的变更和应用的启停,而微服务架构仅仅需要针对有问题的服务进行代码的变更和服务的启停。其他服务可通过重试、熔断等机制实现应用层面的容错。
  • 技术选型灵活:微服务架构下,每个微服务节点可以根据完成需求功能的不同,自由选择最适合的技术栈,即使对单一的微服务节点进行重构,成本也非常低。
  • 开发运维效率更高:每个微服务节点都是一个单一进程,都专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模团队或者个人完全掌控,易于保持高可维护性和开发效率。

Spring Cloud作为目前最流行的微服务开发框架,不是采用了Spring Cloud框架就实现了微服务架构,具备了微服务架构的优势。正确的理解是使用Spring Cloud框架开发微服务架构的系统,使系统具备微服务架构的优势(Spring Cloud就像工具,还需要“做”的过程)。

1.4 什么是Spring Boot?什么是Spring Cloud?

Spring Boot框架是由Pivotal团队提供的全新框架,其设计目的是用来简化基于Spring应用的初始搭建以及开发过程。SpringBoot框架使用了特定的方式来进行应用系统的配置,从而使开发人员不再需要耗费大量精力去定义模板化的配置文件。

Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务注册,服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

1.5 微服务、Spring Boot、Spring Cloud三者之间的关系

  • 思想:微服务是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。
  • 脚手架:Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。
  • 多个组件的集合:Spring Cloud是一个基于Spring Boot实现的服务治理工具包;Spring Boot专注于快速、方便集成的单个微服务个体;Spring Cloud关注全局的服务治理框架。

二、技术解析

2.1 Everything is jar, Everything is http

Spring Boot通过@SpringBootApplication注解标识为Spring Boot应用程序。所有的应用都通过jar包方式编译,部署和运行。

@SpringBootApplication public class Application {         private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);         public static void main(String[] args) {                 SpringApplication.run(Application.class, args);                 LOGGER.info(”启动成功!");         } }

每个Spring Boot的应用都可以通过内嵌web容器的方式提供http服务,仅仅需要在pom文件中依赖spring-boot-start-web即可,原则上微服务架构希望每个独立节点都提供http服务。

    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>

2.2 Spring boot Task 任务启动和定时任务

在Spring Boot需要启动任务时,只要继承CommandLineRunner接口实现其run方法即可。

@SpringBootApplication public class ClientDataListener implements CommandLineRunner    public void run(String... strings) throws Exception {             clientInfoListenerHandler();     }}

在Spring Boot需要执行定时任务时,只需要在定时任务方法上增加@Scheduled(cron = “0 15 0 * * ?”)注解(支持标准cron表达式),并且在服务启动类上增加@EnableScheduling的注解即可。

@SpringBootApplication@EnableSchedulingpublic class Application {         private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);         public static void main(String[] args) {                 SpringApplication.run(Application.class, args);                 LOGGER.info(”启动成功!");         } }
// some class@Scheduled(cron = "0 15 0 * * ?")public void someTimeTask() {    ***}

2.3 Spring boot Actuator 监控

Actuator是spring boot提供的对应用系统自身进行监控的组件,在引入spring-boot-start-web基础上引入spring-boot-starter-actuator即可。

    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-actuator</artifactId>    </dependency>

2.4 Spring cloud Config 配置中心

在我们实现微服务架构时,每个微服务节点都需要自身的相关配置数据项,当节点众多,维护就变得非常困难,因此需要建立一个中心配置服务。

Spring Cloud Config分为两部分。Spring Cloud Config server作为一个服务进程,Spring Cloud Config File为配置文件存放位置。

2.5 Spring cloud Eureka 服务注册中心

服务注册的概念早在微服务架构之前就出现了,微服务架构更是把原先的单一应用节点拆分成非常多的微服务节点。互相之间的调用关系会非常复杂,Spring Cloud Eureka作为注册中心,所有的微服务都可以将自身注册到Spring Cloud Eureka进行统一的管理和访问(Eureka和Zookeeper不同,在AOP原则中选择了OP,更强调服务的有效性)

2.6 Spring cloud Zuul 服务端智能路由

当我们把所有的服务都注册到Eureka(服务注册中心)以后,就涉及到如何调用的问题。Spring Cloud Zuul是Spring Cloud提供的服务端代理组件,可以看做是网关,Zuul通过Eureka获取到可用的服务,通过映射配置,客户端通过访问Zuul来访问实际需要需要访问的服务。所有的服务通过spring.application.name做标识,

不同IP地址,相同spring.application.name就是一个服务集群。当我们增加一个相同spring.application.name的节点,Zuul通过和Eureka通信获取新增节点的信息实现智能路由,增加该类型服务的响应能力。

2.7 Spring cloud Ribbon 客户端智能路由

与Spring Cloud Zuul的服务端代理相对应,Spring Cloud Ribbon提供了客户端代理。在服务端代理中,客户端并不需要知道最终是哪个微服务节点为之提供服务,而客户端代理获取实质提供服务的节点,并选择一个进行服务调用。Ribbon和Zuul相似,也是通过和Eureka(服务注册中心)进行通信来实现客户端智能路由。

2.8 Spring cloud Sleuth 分布式追踪

2.9 Spring cloud Zipkin 调用链

2.10 Spring cloud Feign http客户端

Spring Cloud Feign是一种声明式、模板化的http客户端。 使用Spring Cloud Feign请求远程服务时能够像调用本地方法一样,让开发者感觉不到这是远程方法(Feign集成了Ribbon做负载均衡)。

把远程服务和本地服务做映射

@FeignClient(name = "rabbitmq-http", url = "${SKYTRAIN_RABBITMQ_HTTP}") public interface TaskService {         @RequestMapping(value = "/api/queues", method = RequestMethod.GET)         public String query(@RequestHeader("Authorization") String token); }

以调用本地服务的方式调用远程服务

@Autowired private TaskService taskService; private String queryRabbitmqStringInfo() {         byte[] credentials = Base64 .encodeBase64((rabbitmqHttpUserName + ":" + rabbitmqHttpPassword).getBytes(StandardCharsets.UTF_8));         String token = "Basic " + new String(credentials, StandardCharsets.UTF_8);         return taskService.query(token); }

2.11 Spring cloud Hystrix 断路器

三、微服务实践

3.1 我们开发的几个微服务组件—应用管理中心

应用管理中心可以对每个已经注册的微服务节点进行停止,编译,打包,部署,启动的完整的上线操作。

3.2 我们开发的几个微服务组件—zookeeper数据查询中心

zookeeper数据查询中心根据zookeeper地址,端口,命令获取zookeeper数据信息。

3.3 我们开发的几个微服务组件—微服务健康检测中心

健康检测中心周期性检查每个微服务的状态,当发现有微服务状态处于DOWN或连接超时时,触发报警。

3.4 我们开发的几个微服务组件—定时任务查询中心

// 在BeanPostProcessor子类中拦截@Componentpublic class SkytrainBeanPostProcessor implements BeanPostProcessor, Ordered {    ***    /**     * Bean 实例化之后进行的处理     */    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        beanPostProcessor.postProcessAfter(bean, beanName);        return bean;    }    ***}// 拦截后获取定时任务注解***public Object postProcessAfter(Object bean, String beanName) {    Class targetClass = AopUtils.getTargetClass(bean);    Map annotatedMethods = MethodIntrospector.selectMethods(targetClass,            new MethodIntrospector.MetadataLookup() {                 public Set inspect(Method method) {                     Set scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method,                            Scheduled.class, Schedules.class);                    return (!scheduledMethods.isEmpty() ? scheduledMethods : null);                }            });    if (!annotatedMethods.isEmpty()) {        String className = targetClass.getName();        for (Map.Entry entry : annotatedMethods.entrySet()) {            Method method = entry.getKey();            for (Scheduled scheduled : entry.getValue()) {                String key = className + ":" + method.getName();                String value = scheduled.toString();                taskInfos.put(key, value);            }        }    }    return null;}*** // 获取定时任务后注册***public void taskRegister() {    String nodeInfo = ipAddress + ":" + serverPort + ":";    try {        /**         * 定时任务         */        Map infos = taskInfos;        for (Entry item : infos.entrySet()) {            String taskId = nodeInfo + item.getKey();            String taskParameter = item.getValue();            JSONObject info = new JSONObject();            info.put("taskId", taskId);            info.put("taskParameter", taskParameter);            info.put("applicationName", applicationName);            info.put("taskType", "schedule");            LOGGER.info(info.toString());            zooKeeperExecutor.createZKNode(SKYTRAIN_TASK_ZKNODE_PREFIX + taskId, info.toString());        }    }    catch (Exception ex) {        LOGGER.error("", ex);    }}***

3.5 微服务的分类

  • 微服务平台组件
  • 公共服务组件
  • 基础服务组件/业务服务组件

3.6 整体微服务架构图

作者:梁鑫

来源:宜信技术学院

印度版的“大众点评”如何将 Food Feed 业务从 Redis 迁移到 Cassandra

mumupudding阅读(14)

Zomato 是一家食品订购、外卖及餐馆发现平台,被称为印度版的“大众点评”。目前,该公司的业务覆盖全球24个国家(主要是印度,东南亚和中东市场)。本文将介绍该公司的 Food Feed 业务是如何从 Redis 迁移到 Cassandra 的。

Food Feed 是 Zomato 社交场景中不可或缺的一部分,因为它可以让我们的用户参与其中并与朋友的餐厅评论和图片保持同步,甚至可以通过这个获取餐厅提供的优惠和折扣。开始我们选择 Redis 作为消息 Feed 流的存储引擎,因为在当时的用户场景这是最好的选择。但是随着业务的发展,我们需要更高的可用性和负载支持,而 Redis 不能很好的满足这个需求。虽然我们可以通过丢失一些数据来避免系统的中断,但这不是我们想做的事情。为了确保我们的系统具有高可用性,我们不得不放弃 Redis,而选择 Cassandra 作为其替代品。

Cassandra 非常适合这个用例,因为它是分布式的,就有高可用性等。并且 Cassandra 也可以用于存储时间序列数据 – 这实际上就是我们的Feed 流。在做出这一重大改变之前,我们确实有一些 Cassandra 的使用经验,但对于像 Feed 这样重要的东西来说肯定是不够的。我们必须弄清楚如何顺利的从 Redis 过渡到 Cassandra,并像在 Redis 上那样有效地运行 Feed,并且没有停机时间。

我们开始花时间在 Cassandra 上,在前两周深入探索其配置并调整它以满足我们的要求。接下来,在最终确定 Feed 的架构之前,我们明确了一下两个情况:

  • Feed 流信息一般只用于读取而基本上不会修改。使用 Redis 的时候,我们可以同时读取上百个 keys 而不必担心读取延迟,但是对于Cassandra 而言,连接延迟可能是读取请求过程中一个相当重要的部分。
  • schema 需要足够灵活,以便将来允许 Feed 中新类型的数据。鉴于我们不断迭代并致力于丰富产品体验,因此在 Feed 中添加元素和功能几乎是不可避免的。

我们花了几天时间用于收集了我们项目的数据模式以及各种用户案例,然后开始使用2个数据中心,每个数据中心有3个节点。 我们从 Redis 中迁移大概 6000万条记录到 Cassandra 中用于测试其性能。由于是测试阶段,我们只将一部分流量切入到 Cassandra ,并准备了两个版本的代码,分别写入到 Cassandra 和 Redis 。架构图如下

我们监控系统的延迟和其他问题,令人惊讶的是,我们遇到了写入吞吐量的瓶颈问题。 我们知道 Cassandra 的写入能力非常强,但是我们无法实现我们在各种博客文章和文章中阅读的写入吞吐量。 我们知道出了什么问题,但我们不知道是什么。我们从三个节点中获得的最佳结果是每秒1500次写入,这完全不能满足线上的需求,我们不得不在几个小时内回滚并重新评估。

经过几天的排查,我们意识到问题不在于 Cassandra,而在于 Elastic Block Store(EBS)。EBS是安装在每个EC2实例上的网络驱动器,具有10 Gigabits 的共享带宽和网络流量。当在单个EC2实例上的所有用户之间共享时,该带宽成为我们的瓶颈。为了满足这一需求,我们将数据从基于网络的EBS存储移动到同一EC2实例中的磁盘存储。然后我们在每个服务器上逐个部署由 Cassandra 提供支持的新 Food Feed,以便我们控制吞吐量。很高兴的是,这次成功了。

然后我们开始从我们的生产 Redis 服务器迁移数据(我们花了14个小时来迁移所有内容),在迁移过程中我们没有任何故障或额外负载。这就是 Redis 和 Cassandra 的强大功能。今天,我们的 Food Feed 完全运行在 Cassandra 上,我们在没有停机的情况下完成了这项工作。新的架构如下:

总而言之,通过上面这个项目,我们学到了以下几点:

  • 在写入期间避免数据的读取。“读取”吞吐量大致保持不变,而“写入”规模与节点数量成比例;
  • 避免数据的删除。删除意味着压缩(compaction),当它运行时,节点的资源会被占用;
  • 延迟是一个问题。与Redis相比,Cassandra的连接延迟很高,大约是 Redis 的10x-15x。

作者:明惠

原文链接

本文为云栖社区原创内容,未经允许不得转载。

 

DLL文件和LIB文件生成(关于sqlite数据库)

mumupudding阅读(9)

SQLite是一个开源的跨平台的轻型数据库,WINCE本身也有一个自带的数据库SQLCE ,但占用的资源会比较大。最近项目中考虑用到 SQLite,因此特别研究了一下,下面介绍一下具体的移植方法。

一、下载SQLite源码

       去SQLite官网http://www.sqlite.org/download.htm下载最新的source code。我下载的是sqlite-amalgamation-3071401.zip。解压后会得得到四个文件(shell.c、sqlite3.c、sqlite3.h、sqlite3ext.h)。

二、编译生成WINCE下DLL

      1. 在VS2005下新建一个Win32智能设备项目,选择相应的SDK,并选择应用程序类型为DLL。

      2. 将sqlite-amalgamation-3071401.zip解压后的sqlite3.c、sqlite3.h文件拷贝到工程目录下并添加至工程目录。如下图所示。

            

       3.在预处理中增加SQLITE_ENABLE_RTREE和SQLITE_ENABLE_COLUMN_METADATA宏定义。如图所示:

           

        4.编译项目,会提示“error LNK2019: 无法解析的外部符号 localtime_s,该符号在函数 osLocaltime 中被引用”错误,此错误的解决方法是将localtime_s替换成_localtime64_s即可。

        5.编译成功后会发现目录下会生成SQLite.dll文件,但会发现没有lib文件,这样在使用这个DLL时是没法编译的,所以还需要导出.lib文件,解决方法是在SQLite官网上下载sqlite-dll-win32-x86-XXXXXX.zip文件,解压后将目录下的sqlite3.def文件拷贝到DLL工程目录,在项目属性–链接器–输入–模块定义文件中添加sqlite3.def,如下图所示,然后编译即可。

           

这样SQLite的编译工作就大功告成了。

三、WINCE 下SQLite测试

        新建一个SQLite测试程序,测试代码如下:       

 

[cpp] view plaincopy

  1. #include <windows.h>  
  2.   
  3. // sqlite3的回调函数  
  4. int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )  
  5.   
  6. int main(int argc,char* argv[])  
  7. {  
  8.     sqlite3 * db = NULL; //声明sqlite关键结构指针  
  9.     int result;  
  10.     // 打开或创建数据库  
  11.     result = sqlite3_open(“NAND2\\sqlite.db”, &db );  
  12.     if( result != SQLITE_OK )  
  13.     {  
  14.         //数据库打开失败  
  15.         return -1;  
  16.     }  
  17.     char * errmsg = NULL;  
  18.     // 数据库操作代码  
  19. #if 1  
  20.     // 创建一个测试表,表名叫 MyTable,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加  
  21.     result = sqlite3_exec( db, “create table MyTable( ID integer primary key autoincrement, name nvarchar(32) )”, NULL, NULL, &errmsg );  
  22.     if(result != SQLITE_OK )  
  23.     {  
  24.         printf(“创建表失败,错误码:%d,错误原因:%s\n”, result, errmsg );  
  25.     }  
  26.     // 插入记录  
  27.     result = sqlite3_exec( db, “insert into MyTable( name ) values ( ‘张三’ )”, 0, 0, &errmsg );  
  28.     if(result != SQLITE_OK )  
  29.     {  
  30.         printf(“插入记录失败,错误码:%d,错误原因:%s\n”, result, errmsg );  
  31.     }  
  32.     // 插入记录  
  33.     result = sqlite3_exec( db, “insert into MyTable( name ) values ( ‘李四’ )”, 0, 0, &errmsg );  
  34.     if(result != SQLITE_OK )  
  35.     {  
  36.         printf(“插入记录失败,错误码:%d,错误原因:%s\n”, result, errmsg );  
  37.     }  
  38. #endif  
  39.     // 开始查询数据库  
  40.     result = sqlite3_exec( db, “select * from MyTable”, SQLiteQueryResultCallBack, NULL, &errmsg );  
  41.     // 关闭数据库  
  42.     sqlite3_close( db );  
  43.   
  44.     return 0;  
  45. }  
  46.   
  47. // sqlite3的回调函数  
  48. int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )  
  49. {  
  50.     printf( “******************************\n” );  
  51.     printf(“记录包含 %d 个字段\n”, n_column );  
  52.     for(int i = 0 ; i < n_column; i ++ )  
  53.     {  
  54.         printf( “字段名:%s 字段值:%s\n”, column_name[i], column_value[i] );  
  55.     }  
  56.     printf( “******************************\n” );  
  57.     return 0;  
  58. }  
 
  1. #include <windows.h>

  2.  
  3. // sqlite3的回调函数

  4. int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )

  5.  
  6. int main(int argc,char* argv[])

  7. {

  8. sqlite3 * db = NULL; //声明sqlite关键结构指针

  9. int result;

  10. // 打开或创建数据库

  11. result = sqlite3_open("NAND2\\sqlite.db", &db );

  12. if( result != SQLITE_OK )

  13. {

  14. //数据库打开失败

  15. return -1;

  16. }

  17. char * errmsg = NULL;

  18. // 数据库操作代码

  19. #if 1

  20. // 创建一个测试表,表名叫 MyTable,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加

  21. result = sqlite3_exec( db, "create table MyTable( ID integer primary key autoincrement, name nvarchar(32) )", NULL, NULL, &errmsg );

  22. if(result != SQLITE_OK )

  23. {

  24. printf("创建表失败,错误码:%d,错误原因:%s\n", result, errmsg );

  25. }

  26. // 插入记录

  27. result = sqlite3_exec( db, "insert into MyTable( name ) values ( '张三' )", 0, 0, &errmsg );

  28. if(result != SQLITE_OK )

  29. {

  30. printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg );

  31. }

  32. // 插入记录

  33. result = sqlite3_exec( db, "insert into MyTable( name ) values ( '李四' )", 0, 0, &errmsg );

  34. if(result != SQLITE_OK )

  35. {

  36. printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg );

  37. }

  38. #endif

  39. // 开始查询数据库

  40. result = sqlite3_exec( db, "select * from MyTable", SQLiteQueryResultCallBack, NULL, &errmsg );

  41. // 关闭数据库

  42. sqlite3_close( db );

  43.  
  44. return 0;

  45. }

  46.  
  47. // sqlite3的回调函数

  48. int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )

  49. {

  50. printf( "******************************\n" );

  51. printf("记录包含 %d 个字段\n", n_column );

  52. for(int i = 0 ; i < n_column; i ++ )

  53. {

  54. printf( "字段名:%s 字段值:%s\n", column_name[i], column_value[i] );

  55. }

  56. printf( "******************************\n" );

  57. return 0;

  58. }

四、SQLite可视化管理工具

 

 

 

        SQLite本身没有可视化管理工具,只提供了一个命令行的管理工具SQLite.exe。有一个第三方的可视化管理工具Sqlite Expert,用着还可以,下载地址: http://www.sqliteexpert.com/download.html

Synchronized 偏向锁、轻量级锁、自旋锁、锁消除

mumupudding阅读(15)

 

 

一、重量级锁

  上篇文章中向大家介绍了Synchronized的用法及其实现的原理。现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。

 

二、轻量级锁 

  锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。JDK 1.6中默认是开启偏向锁和轻量级锁的,我们也可以通过-XX:-UseBiasedLocking来禁用偏向锁。锁的状态保存在对象的头文件中,以32位的JDK为例:

锁状态

25 bit

4bit

1bit

2bit

23bit

2bit

是否是偏向锁

锁标志位

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向互斥量(重量级锁)的指针

10

GC标记

11

偏向锁

线程ID

Epoch

对象分代年龄

1

01

无锁

对象的hashCode

对象分代年龄

0

01

  “轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的。但是,首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

1轻量级锁的加锁过程

  (1)在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word。这时候线程堆栈与对象头的状态如图2.1所示。

  (2)拷贝对象头中的Mark Word复制到锁记录中。

  (3)拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤(3),否则执行步骤(4)。

  (4)如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图2.2所示。

  (5)如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程。

Synchronized 偏向锁、轻量级锁、自旋锁、锁消除

2、轻量级锁的解锁过程:

  (1)通过CAS操作尝试把线程中复制的Displaced Mark Word对象替换当前的Mark Word。

  (2)如果替换成功,整个同步过程就完成了。

  (3)如果替换失败,说明有其他线程尝试过获取该锁(此时锁已膨胀),那就要在释放锁的同时,唤醒被挂起的线程。

三、偏向锁

  引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。上面说过,轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。

1、偏向锁获取过程:

  (1)访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01——确认为可偏向状态。

  (2)如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤(5),否则进入步骤(3)。

  (3)如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行(5);如果竞争失败,执行(4)。

  (4)如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。

  (5)执行同步代码。

2、偏向锁的释放:

  偏向锁的撤销在上述第四步骤中有提到偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

3、重量级锁、轻量级锁和偏向锁之间转换

Synchronized 偏向锁、轻量级锁、自旋锁、锁消除

四、其他优化 

1适应性自旋(Adaptive Spinning):从轻量级锁获取的流程中我们知道当线程在获取轻量级锁的过程中执行CAS操作失败时,是要通过自旋来获取重量级锁的。问题在于,自旋是需要消耗CPU的,如果一直获取不到锁的话,那该线程就一直处在自旋状态,白白浪费CPU资源。解决这个问题最简单的办法就是指定自旋的次数,例如让其循环10次,如果还没获取到锁就进入阻塞状态。但是JDK采用了更聪明的方式——适应性自旋,简单来说就是线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。

2、锁粗化(Lock Coarsening):锁粗化的概念应该比较好理解,就是将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成一个范围更大的锁。举个例子:

package com.paddx.test.string;
  
  public class StringBufferTest {
      StringBuffer stringBuffer = new StringBuffer();
  
      public void append(){
          stringBuffer.append("a");
          stringBuffer.append("b");
          stringBuffer.append("c");
     }
 }

这里每次调用stringBuffer.append方法都需要加锁和解锁,如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。

3、锁消除(Lock Elimination):锁消除即删除不必要的加锁操作。根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁。看下面这段程序:

package com.paddx.test.concurrent;
  
  public class SynchronizedTest02 {
  
      public static void main(String[] args) {
          SynchronizedTest02 test02 = new SynchronizedTest02();
          //启动预热
          for (int i = 0; i < 10000; i++) {
              i++;
         }
         long start = System.currentTimeMillis();
         for (int i = 0; i < 100000000; i++) {
             test02.append("abc", "def");
         }
         System.out.println("Time=" + (System.currentTimeMillis() - start));
     }
 
     public void append(String str1, String str2) {
         StringBuffer sb = new StringBuffer();
         sb.append(str1).append(str2);
     }
 }

虽然StringBuffer的append是一个同步方法,但是这段程序中的StringBuffer属于一个局部变量,并且不会从该方法中逃逸出去,所以其实这过程是线程安全的,可以将锁消除。下面是我本地执行的结果:

为了尽量减少其他因素的影响,这里禁用了偏向锁(-XX:-UseBiasedLocking)。通过上面程序,可以看出消除锁以后性能还是有比较大提升的。

五、总结 

  本文重点介绍了JDk中采用轻量级锁和偏向锁等对Synchronized的优化,但是这两种锁也不是完全没缺点的,比如竞争比较激烈的时候,不但无法提升效率,反而会降低效率,因为多了一个锁升级的过程,这个时候就需要通过-XX:-UseBiasedLocking来禁用偏向锁。下面是这几种锁的对比:

优点

缺点

适用场景

偏向锁

加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。

如果线程间存在锁竞争,会带来额外的锁撤销的消耗。

适用于只有一个线程访问同步块场景。

轻量级锁

竞争的线程不会阻塞,提高了程序的响应速度。

如果始终得不到锁竞争的线程使用自旋会消耗CPU。

追求响应时间。

同步块执行速度非常快。

重量级锁

线程竞争不使用自旋,不会消耗CPU。

线程阻塞,响应时间缓慢。

追求吞吐量。

同步块执行速度较长。

 

Linux系统中五款好用的日志分析工具

mumupudding阅读(9)

监控网络活动是一项繁琐的工作,但有充分的理由这样做。例如,它允许你查找和调查工作站和连接到网络的设备及服务器上的可疑登录,同时确定管理员滥用了什么。你还可以跟踪软件安装和数据传输,以实时识别潜在问题,而不是在损坏发生后才进行跟踪。
这些日志还有助于使你的公司遵守适用于在欧盟范围内运营的任何实体的通用数据保护条例(GFPR)。如果你的网站在欧盟可以浏览,那么你就有遵守的该条例的资格。
日志记录,包括跟踪和分析,应该是任何监控基础设置中的一个基本过程。要从灾难中恢复 SQL Server 数据库,需要事务日志文件。此外,通过跟踪日志文件,DevOps 团队和数据库管理员(DBA)可以保持最佳的数据库性能,又或者,在网络攻击的情况下找到未经授权活动的证据。因此,定期监视和分析系统日志非常重要。这是一种重新创建导致出现任何问题的事件链的可靠方式。
现在有很多开源日志跟踪器和分析工具可供使用,这使得为活动日志选择合适的资源比你想象的更容易。自由和开源软件社区提供的日志设计适用于各种站点和操作系统。以下是五个我用过的最好的工具,它们并没有特别的顺序。

Graylog

Graylog 于 2011 年在德国创立,现在作为开源工具或商业解决方案提供。它被设计成一个集中式日志管理系统,接受来自不同服务器或端点的数据流,并允许你快速浏览或分析该信息。
Graylog 在系统管理员中有着良好的声誉,因为它易于扩展。大多数 Web 项目都是从小规模开始的,但它们可能指数级增长。Graylog 可以均衡后端服务网络中的负载,每天可以处理几 TB 的日志数据。
IT 管理员会发现 Graylog 的前端界面易于使用,而且功能强大。Graylog 是围绕仪表板的概念构建的,它允许你选择你认为最有价值的指标或数据源,并快速查看一段时间内的趋势。
当发生安全或性能事件时,IT 管理员希望能够尽可能地根据症状追根溯源。Graylog 的搜索功能使这变得容易。它有内置的容错功能,可运行多线程搜索,因此你可以同时分析多个潜在的威胁。
最好用的开源日志分析工具最好用的开源日志分析工具

Nagios

Nagios 始于 1999 年,最初是由一个开发人员开发的,现在已经发展成为管理日志数据最可靠的开源工具之一。当前版本的 Nagios 可以与运行 Microsoft Windows、Linux 或 Unix 的服务器集成。
它的主要产品是日志服务器,旨在简化数据收集并使系统管理员更容易访问信息。Nagios 日志服务器引擎将实时捕获数据,并将其提供给一个强大的搜索工具。通过内置的设置向导,可以轻松地与新端点或应用程序集成。
Nagios 最常用于需要监控其本地网络安全性的组织。它可以审核一系列与网络相关的事件,并帮助自动分发警报。如果满足特定条件,甚至可以将 Nagios 配置为运行预定义的脚本,从而允许你在人员介入之前解决问题。
作为网络审计的一部分,Nagios 将根据日志数据来源的地理位置过滤日志数据。这意味着你可以使用地图技术构建全面的仪表板,以了解 Web 流量是如何流动的。
最好用的开源日志分析工具最好用的开源日志分析工具

Elastic Stack (ELK Stack)

Elastic Stack,通常称为 ELK Stack,是需要筛选大量数据并理解其日志系统的组织中最受欢迎的开源工具之一(这也是我个人的最爱)。
它的主要产品由三个独立的产品组成:Elasticsearch、Kibana 和 Logstash:
顾名思义, Elasticsearch 旨在帮助用户使用多种查询语言和类型在数据集之中找到匹配项。速度是它最大的优势。它可以扩展成由数百个服务器节点组成的集群,轻松处理 PB 级的数据。
Kibana 是一个可视化工具,与 Elasticsearch 一起工作,允许用户分析他们的数据并构建强大的报告。当你第一次在服务器集群上安装 Kibana 引擎时,你会看到一个显示着统计数据、图表甚至是动画的界面。
ELK Stack 的最后一部分是 Logstash,它作为一个纯粹的服务端管道进入 Elasticsearch 数据库。你可以将 Logstash 与各种编程语言和 API 集成,这样你的网站和移动应用程序中的信息就可以直接提供给强大的 Elastic Stalk 搜索引擎中。
ELK Stack 的一个独特功能是,它允许你监视构建在 WordPress 开源网站上的应用程序。与跟踪管理日志和 PHP 日志的大多数开箱即用的安全审计日志工具相比,ELK Stack 可以筛选 Web 服务器和数据库日志。
糟糕的日志跟踪和数据库管理是导致网站性能不佳的最常见原因之一。没有定期检查、优化和清空数据库日志,不仅会降低站点的运行速度,还可能导致其完全崩溃。因此,ELK Stack 对于每个 WordPress 开发人员的工具包来说都是一个优秀的工具。
最好用的开源日志分析工具最好用的开源日志分析工具

LOGalyze

LOGalyze 是一个位于匈牙利的组织,它为系统管理员和安全专家构建开源工具,以帮助他们管理服务器日志,并将其转换为有用的数据点。其主要产品可供个人或商业用户免费下载。
LOGalyze 被设计成一个巨大的管道,其中多个服务器、应用程序和网络设备可以使用简单对象访问协议(SOAP)方法提供信息。它提供了一个前端界面,管理员可以登录界面来监控数据集并开始分析数据。
在 LOGalyze 的 Web 界面中,你可以运行动态报告,并将其导出到 Excel 文件、PDF 文件或其他格式。这些报告可以基于 LOGalyze 后端管理的多维统计信息。它甚至可以跨服务器或应用程序组合数据字段,借此来帮助你发现性能趋势。
LOGalyze 旨在不到一个小时内完成安装和配置。它具有预先构建的功能,允许它以法律所要求的格式收集审计数据。例如,LOGalyze 可以很容易地运行不同的 HIPAA 报告,以确保你的组织遵守健康法律并保持合规性。
最好用的开源日志分析工具最好用的开源日志分析工具

Fluentd

如果你所在组织的数据源位于许多不同的位置和环境中,那么你的目标应该是尽可能地将它们集中在一起。否则,你将难以监控性能并防范安全威胁。
Fluentd 是一个强大的数据收集解决方案,它是完全开源的。它没有提供完整的前端界面,而是作为一个收集层来帮助组织不同的管道。Fluentd 在被世界上一些最大的公司使用,但是也可以在较小的组织中实施。
Fluentd 最大的好处是它与当今最常用的技术工具兼容。例如,你可以使用 Fluentd 从 Web 服务器(如 Apache)、智能设备传感器和 MongoDB 的动态记录中收集数据。如何处理这些数据完全取决于你。
Fluentd 基于 JSON 数据格式,它可以与由卓越的开发人员创建的 500 多个插件一起使用。这使你可以将日志数据扩展到其他应用程序中,并通过最少的手工操作从中获得更好的分析。
最好用的开源日志分析工具最好用的开源日志分析工具