carlosfu


  • 首页

  • 分类

  • 归档

  • 标签

Redis的Linux系统优化

发表于 2017-02-16   |   分类于 Redis开发与运维   |     |   阅读次数

声明:本文内容来自《Redis开发与运维》一书第12章,如转载请声明。

通常来看,Redis开发和运维人员更加关注的是Redis本身的一些配置优化,例如AOF和RDB的配置优化、数据结构的配置优化等,但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心,然而事实证明一个良好的系统操作配置能够为Redis服务良好运行保驾护航。

众所周知Redis的作者对于Windows操作系统并不感冒,目前大部分公司都会将Web服务器、数据库服务器等部署在Linux操作系统上,Redis也不例外。所以接下来介绍Linux操作系统如何优化Redis,包含如下七个方面。

一. 内存分配控制
二. swappiness
三. Transparent Huge Pages
四. OOM killer
五. 使用NTP
六. ulimit
七. TCP backlog

一. 内存分配控制

1. vm.overcommit_memory

Redis在启动时可能会出现这样的日志:

1
2
# WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the
command 'sysctl vm.overcommit_memory=1' for this to take effect.
阅读全文 »

quartz入门

发表于 2016-12-26   |   分类于 quartz   |     |   阅读次数

quartz是Java中很强大的调度工具,它的使用比较简单,但是实际上无论从源码量以及实际生产中的规模化应用看,实际上quartz相当复杂,要想用好实际上也是不容易的。

下图是quartz的整体架构图,基本展示了quartz组件的相关关系。

  • JobDetail是具体的任务
  • Trigger是触发器,确定什么时候去做什么
  • scheduler是调度器,整体上调度trigger和job。

quartz原生代码都是使用Builder模式来定义。

1.quartz maven依赖

quartz似乎更新不是很频繁,官网的最新版本是2.2.3,但是用的比较广泛的是2.2.1。为了演示项目,加入了junit和logback。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencies>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
</dependencies>

2.定义Job

Job就是任务的定义,比如定义了一个任务是从指定host:port的redis中收集慢查询,其中host:port可以通过定义属性或者通过JobExecutionContext获取JobDataMap来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.carlosfu.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author fulei.fl 2017年12月29日 下午3:39:32
*/
public class HelloJob implements Job {
private Logger logger = LoggerFactory.getLogger(HelloJob.class);
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("hello quartz!");
}
}

3.定义JobDetail

而JobDetail就是具体的任务了,每个任务可以用name和group进行区分(withIdentity)。

1
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "group1").build();

4.定义Trigger

Trigger包含了两种类型SimpleTrigger和CronTrigger(可以通过cron表达式设置,个人认为可以基本代替SimpleTrigger的功能)。

1
2
3
4
5
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("helloTrigger", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2).repeatForever())
.build();

5.定义Scheduler

1
2
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

启动

1
scheduler.start();

调度任务

1
scheduler.scheduleJob(jobDetail, trigger);

quartz-2.crontab语法

发表于 2016-12-26   |   分类于 quartz   |     |   阅读次数

quartz的crontab功能是它的强大之处,它的语法和linux有些类似,但是又有一些不同,例如linux无法精确到秒,而quartz是支持的,本文直接通过例子进行说明。

1. 语法简介

1
[秒] [分] [小时] [日] [月] [星期] [年]

每个位置可以支持设置的值如下:

有几点需要说明

  • 除了年,每个位置是必须的。
  • 每个位置如果无特殊值,可以用*代替,代表每。
  • 除了一些数字,每个位置还允许一些特殊字符。

2. 例子。

下面直接用例子来说明,具体如下:

(1) 每秒执行

1
* * * * * *

啰嗦解释一下就是每个月、每个星期、每天、每小时、每分钟、每秒。所以其实就是每秒。但是如果运行程序就会发现报如下错误, 似乎提示是星期和日是一个矛盾体。

1
2
3
4
5
6
7
8
Exception in thread "main" java.lang.RuntimeException: CronExpression '* * * * * *' is invalid.
at org.quartz.CronScheduleBuilder.cronSchedule(CronScheduleBuilder.java:111)
at com.carlosfu.main.HelloScheduler.main(HelloScheduler.java:28)
Caused by: java.text.ParseException: Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.
at org.quartz.CronExpression.buildExpression(CronExpression.java:511)
at org.quartz.CronExpression.<init>(CronExpression.java:276)
at org.quartz.CronScheduleBuilder.cronSchedule(CronScheduleBuilder.java:107)
... 1 more

因为日和星期是一个矛盾体,所以可以用一个?代替即可,所以下面两个的结果是一样的,都代表每秒。

(a) 每个月的每个星期
1
* * * ? * *
(b) 每个月的每天
1
* * * * * ?
(c)下面两种情况都是错误的
1
2
* * * * ? *
* * * * ? ?

(2) 每分钟执行一次

下面是不是呢?其实仔细想想“分钟”位置0/1和*的效果是一样的

1
* 0/1 * ? * *

也就是说如果想实现每分钟执行一次,要在秒的位置给出具体的值,例如

每分钟的第59秒执行

1
59 0/1 * ? * *

每分钟的第0秒执行

1
0 0/1 * ? * *

有个小技巧,如果需求是只要保证每分钟完成,而不在意在哪秒完成,在大量trigger的情况下,可以对秒做打散操作。

1
2
3
public static String getSlowLogCron() {
return new Random().nextInt(60) + " 0/1 * ? * *";
}

如果

1
0 0/1 * ? * *

具体日志如下

1
2
3
4
5
17:27:00.005 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:28:00.004 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
17:29:00.006 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!
17:30:00.003 [DefaultQuartzScheduler_Worker-4] INFO com.carlosfu.job.HelloJob - hello quartz!
17:31:00.002 [DefaultQuartzScheduler_Worker-5] INFO com.carlosfu.job.HelloJob - hello quartz!

如果想每5秒执行一次,可以如下:

1
0/5 * * ? * *

如果想每5秒执行一次,但是从每分钟的第20秒到第60秒,可以这么配置

1
20/5 * * ? * *

如果当前的秒小于20,那么就等到执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
now: 2018-01-01 17:35:11
17:35:20.011 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:25.006 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:30.001 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:35.002 [DefaultQuartzScheduler_Worker-4] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:40.002 [DefaultQuartzScheduler_Worker-5] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:45.005 [DefaultQuartzScheduler_Worker-6] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:50.006 [DefaultQuartzScheduler_Worker-7] INFO com.carlosfu.job.HelloJob - hello quartz!
17:35:55.006 [DefaultQuartzScheduler_Worker-8] INFO com.carlosfu.job.HelloJob - hello quartz!
17:36:20.001 [DefaultQuartzScheduler_Worker-9] INFO com.carlosfu.job.HelloJob - hello quartz!
17:36:25.004 [DefaultQuartzScheduler_Worker-10] INFO com.carlosfu.job.HelloJob - hello quartz!
17:36:30.002 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:36:35.005 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!

如果当前秒超过20,可以在执行周期内执行。

1
2
3
4
now: 2018-01-01 17:37:24
17:37:25.007 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:37:30.004 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
17:37:35.006 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!
1
2
3
4
now: 2018-01-01 17:38:38
17:38:40.008 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:38:45.003 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
17:38:50.003 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!

(3)每天10点20分执行

1
0 20 10 ? * *

(4) 每天下午2点~3点(整点开始,每隔五分钟执行)

1
0 0/5 14 ? * *

(5) 每月的第三周的星期五的10点20分执行

1
0 20 10 ? * 6#3

(6) 每周一到周五的10点20分执行

1
0 20 10 ? * MON-FRI

注意:星期是不分大小写。

3. 其他

特殊字符的解释如下,如果用到的时候可以看下。

也有在线的crontab生成工具:

0和30分执行

1
0/30 * * ? * *

5和35分执行

1
5/30 * * ? * *

1
15/50 * * ? * *
1
2
3
4
5
now: 2018-01-01 18:02:35
18:03:15.006 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
18:04:15.005 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
18:05:15.005 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!
18:06:15.003 [DefaultQuartzScheduler_Worker-4] INFO com.carlosfu.job.HelloJob - hello quartz!
1
2
3
4
now: 2018-01-01 17:55:53
17:56:00.010 [DefaultQuartzScheduler_Worker-1] INFO com.carlosfu.job.HelloJob - hello quartz!
17:56:30.004 [DefaultQuartzScheduler_Worker-2] INFO com.carlosfu.job.HelloJob - hello quartz!
17:57:00.001 [DefaultQuartzScheduler_Worker-3] INFO com.carlosfu.job.HelloJob - hello quartz!

Hbase学习1.2-伪分布式安装

发表于 2016-11-28   |   分类于 HBase   |     |   阅读次数
1
没有单独安装hadoop、zookeeper随便找了个单点,后续继续安装

一. 机器

  • 10.16.14.182
  • 10.16.14.153
  • 10.10.53.159

二. 资源分布

  • 10.16.14.182: hmaster、regionserver、zookeeper
  • 10.16.14.153: regionserver
  • 10.10.53.159: regionserver

hadoop:不安装,直接走本地文件系统

阅读全文 »

Redis无限全量复制问题分析与优化

发表于 2016-11-24   |   分类于 Redis   |     |   阅读次数

本文部分内容来自《Redis开发与运维》一书,转载请声明。

一、现象和危害

线上有台机器内存接近了90%,总内存为24G,整个部署如下图:

现要将Redis-2迁移走,由于特殊原因此节点没有slave节点,需要添加一个slave节点,然后做failover操作。

通过对日志的观察,发现主从不停地做全量复制。

阅读全文 »

docker初识

发表于 2016-11-24   |   分类于 docker   |     |   阅读次数

一、docker的三点好处

  • 统一环境和配置:想想平时怎么迁移服务。
  • 高效伸缩性:想想平时怎么扩容
  • 资源隔离(使用lxc):想想单机多部署。

二、docker在linux上安装方法

为了简单(非生产环境),使用yum进行安装。

所有安装的前提是内核版本要在大于等于3.10

1
2
[root@iZ2ze0ay4bt2m4mafbw5euZ ~]# uname -r
3.10.0-693.2.2.el7.x86_64
阅读全文 »

Redis客户端常见异常分析

发表于 2016-11-17   |   分类于 Redis开发与运维   |     |   阅读次数

本文部分内容来自《Redis开发与运维》一书,转载请声明。

在Redis客户端的使用过程中,无论是客户端使用不当或者Redis服务端出现问题,客户端会反应出一些异常,下面分析一下Jedis使用过程中常见的异常情况:

一.无法从连接池获取到连接

JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就需要进行等待(例如设置了maxWaitMillis>0),如果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出如下异常。

1
2
3
4
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
…
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)

还有一种情况,就是设置了blockWhenExhausted=false,那么调用者发现池子中没有资源时,会立即抛出异常不进行等待,下面的异常就是blockWhenExhausted=false时的效果。

1
2
3
4
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
…
Caused by: java.util.NoSuchElementException: Pool exhausted
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
阅读全文 »

Redis Cluster多机房高可用实现--基于客户端

发表于 2016-11-03   |   分类于 Redis   |     |   阅读次数
1
本文以Redis-Cluster为例子,实际使用中Redis-Sentinel和Redis Standalone也是一样的。

一、现有问题

由于Redis本身的一些特性(例如复制)以及使用场景,造成Redis不太适合部署在不同的机房,所以通常来看Redis集群都是在同一个机房部署的。虽然Redis集群自身已经具备了高可用的特性,即使几个Redis节点异常或者挂掉,Redis Cluster也会实现故障自动转移,对应用方来说也可以在很短时间内恢复故障。但是如果发生了机房故障(断电、断网等极端情况),如果应用方降级或者容错机制做的不好甚至业务本身不能降级,或者会丢失重要数据,或者可能瞬间会跑满应用的线程池造成服务不可用,对于一些重要的服务来说是非常致命的。

为了应对像机房故障这类情况,保证应用方在这种极端情况下,仍然可以正常服务(系统正常运行、数据正常),所以需要给出一个Redis跨机房的方案。

二、实现思路和目标:

1.思路

  • 使用CacheCloud开通两个位于两个不同机房的Redis-Cluster集群(例如:兆维、北显):一个叫major,作为主Redis服务,一个叫minor,作为备用Redis服务。
  • 开发定制版的客户端,利用netflix的hystrix组件能够解除依赖隔离的特性,在major出现故障时,将故障隔离,并将请求自动转发到minor,并且对于应用的主线程池没有影响。(有关hystrix的请求流程流程见下图,有关hystrix使用请参考:http://hot66hot.iteye.com/blog/2155036

阅读全文 »

小坑

发表于 2016-10-24   |   分类于 小坑   |     |   阅读次数

1.application.properties

spring-boot中,application.properties中的值,无论是字符串或者数字,都不要加双引号,例如

1
user.name=carlos

不要写成

1
user.name="carlos"

配置atlas时候,由于添加了双引号的application.server.name造成启动一直保存,直到debug错误中才发现原来是这个造成的。

1
spring.application.name = "carlosfu-atlas-test"

OpenResty-4.OpenResty使用Kafka

发表于 2016-09-29   |   分类于 OpenResty   |     |   阅读次数

openresty官方模块

1
https://github.com/openresty/lua-nginx-module#readme

其中没有kafka的模块,可以在google上搜索可以查到lua-resty-kafka

1
https://github.com/doujiang24/lua-resty-kafka

1.安装lua-resty-kafka

1
2
3
4
5
6
wget https://github.com/doujiang24/lua-resty-kafka/archive/master.zip
unzip lua-resty-kafka-master.zip -d /opt/nginx/
#拷贝lua-resty-kafka到openresty
mkdir /opt/openresty/lualib/kafka
cp -rf /opt/nginx/lua-resty-kafka-master/lib/resty /opt/openresty/lualib/kafka/
阅读全文 »
1…3456
carlosfu

carlosfu

55 日志
22 分类
47 标签
cachecloud shiye xiaodada
© 2018 carlosfu
由 Hexo 强力驱动
主题 - NexT.Mist