nginx限制IP恶意调用短信接口处理方法

真实案例:

查看nginx日志,发现别有用心的人恶意调用API接口刷短信:

30966487 115.213.229.38 "-" [05/Jun/2018:14:37:29 +0800] 0.003 xxxxxx.com "POST /xxx/sendCheckCode HTTP/1.1" 401 200 46 xx.xx.xx.xx:0000 0.003 200 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"
30963985 60.181.111.140 "-" [05/Jun/2018:14:37:29 +0800] 0.004 xxxxxx.com "POST /xxx/sendCheckCode HTTP/1.1" 401 200 46 xx.xx.xx.xx:0000 0.004 200 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"
30959954 220.190.18.25 "-" [05/Jun/2018:14:37:29 +0800] 0.003 xxxxxx.com "POST /xxx/sendCheckCode HTTP/1.1" 401 200 46 xx.xx.xx.xx:0000 0.003 200 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"

思考了几种方案,最终考虑使用ip黑名单的方式:

处理方法:

一、nginx黑名单方式:

1、过滤日志访问API接口的IP,统计每10分钟调用超过100次的IP,直接丢进nginx的访问黑名单

2、具体步骤:

编写shell脚本:

vim /shell/nginx_cutaccesslog.sh
#!/bin/bash
log_path=/xxx/nginx/logs
date=`date -d "10 min ago" +%Y%m%d-%H:%M:%S`
nginxpid=`cat ${log_path}/nginx.pid`
cd ${log_path}
#过滤access.log中正常访问API接口并在10分钟(下面是日志切割,再做个定时任务每10分钟执行一次,就可以实现了)内访问量最高的30个IP,取值如果此IP访问量大于100次,则把此IP放入黑名单
cat access.log | grep  sendCheckCode  | grep -v 403 | awk '{print $2}'|sort|uniq -c | sort -k1 -n | tail -30 | awk '{if($1>100) print "deny "$2";"}' > ../conf/denyip.conf
#日志切割,做定时任务,每10分钟执行一次
mv ${log_path}/access.log ${log_path}/accesslog.bak/access_${date}.log
../sbin/nginx -s reload

 

可自己定义时间间隔和访问量,也可取消筛选访问量最高的30个,直接取值每10分钟访问接口超过100次的

其中:”grep -v 403″ 是把已经禁止访问的IP给过滤掉,只筛选正常访问的

3、修改nginx.conf

在http模块加入:

include       denyip.conf;

重新加载nginx生效。

4、添加计划任务:

*/10 * * * * /bin/bash /shell/nginx_cutaccesslog.sh > /dev/null 2>&1

5、验证:

[root@xxx logs]# ll accesslog.bak/
-rw-r--r-- 1 root   root    2663901 Jun  5 15:10 access_20180605-15:00:01.log
-rw-r--r-- 1 root   root   13696947 Jun  5 15:20 access_20180605-15:10:01.log
-rw-r--r-- 1 root   root   13265509 Jun  5 15:30 access_20180605-15:20:01.log
-rw-r--r-- 1 root   root   13846297 Jun  5 15:40 access_20180605-15:30:01.log
[root@xxx logs]# cat ../conf/denyip.conf 
…………
…………
deny 112.12.137.28;
deny 183.167.237.229;
deny 111.41.43.58;
deny 115.217.117.159;
deny 219.133.100.133;
deny 171.221.254.115;
deny 60.184.131.6;
…………
…………

再查看已经禁用IP的访问日志,则会返回403错误:

[root@xxx logs]# tail -f access.log | grep "60.184.131.6"
31268622 60.184.131.6 "-" [05/Jun/2018:15:47:34 +0800] 0.000 xxxxxx.com "POST /xxxxxx/sendCheckCode HTTP/1.1" 377 403 168 - - - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"
31268622 60.184.131.6 "-" [05/Jun/2018:15:47:35 +0800] 0.000 xxxxxx.com "POST /xxxxxx/sendCheckCode HTTP/1.1" 377 403 168 - - - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"
31268622 60.184.131.6 "-" [05/Jun/2018:15:47:35 +0800] 0.000 xxxxxx.com "POST /xxxxxx/sendCheckCode HTTP/1.1" 377 403 168 - - - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0" "https://xxxxxx/sendCheckCode"

二、限制IP请求数:

处理这种情况的方法还有一种是限制单 IP 单位时间的请求数,以及单 IP 的并发连接数

此方法没有实际运用,因为感觉这种方法会误杀正常的访问用户

写一下此方法的大概配置,http模块加入:

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=8r/s;
    server {
        location /search/ {
            limit_req zone=one burst=5;
        }

如何估算 limit_req_zone size:

一兆字节区域可以保持大约1万6064字节的状态。

那么 10M 就可以存储 16 万的 IP 统计信息, 这个对普通应用足够了,16 万每秒的 UV,已经超级厉害了。

如果 size 的大小如果设置小了, 例如设置成 1M,那么当一秒内的请求 IP 数超过 16000 的时候,超出的 IP 对应的用户看到的均为 503 Service Temporarily Unavailable 页面了。参考, 漏桶算法 Leaky Bucket。 同时,rate 的单位用 r/s 非常合适,如果换成按天,按小时计数,10M 的内存肯定不够用。

如何估算 limit_req_zone rate:

首先需要知道的是,普通浏览器的同时并发数量。按照 Dropbox 技术博客里所谈到的,目前主流浏览器限制 AJAX 对同一个子域名的并发连接数是6个。IE 6,IE 7 是两个。

大多数浏览器每个主机名都有6个并发连接的限制。

 

Spring缓存注解@Cacheable、@CacheEvict、@CachePut使用

从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。

使用Spring Cache需要我们做两方面的事:

n  声明某些方法使用缓存

n  配置Spring对Cache的支持

和Spring对事务管理的支持一样,Spring对Cache的支持也有基于注解和基于XML配置两种方式。下面我们先来看看基于注解的方式。

 

1       基于注解的支持

Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。

1.1    @Cacheable

@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。

1.1.1  value属性指定Cache名称

value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

@Cacheable(“cache1”)//Cache是发生在cache1上的

public User find(Integer id) {

returnnull;

}

 

@Cacheable({“cache1”, “cache2”})//Cache是发生在cache1和cache2上的

public User find(Integer id) {

returnnull;

}

 

1.1.2  使用key属性自定义key

key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。

自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

@Cacheable(value=”users”, key=”#id”)

public User find(Integer id) {

returnnull;

}

 

@Cacheable(value=”users”, key=”#p0″)

public User find(Integer id) {

returnnull;

}

 

@Cacheable(value=”users”, key=”#user.id”)

public User find(User user) {

returnnull;

}

 

@Cacheable(value=”users”, key=”#p0.id”)

public User find(User user) {

returnnull;

}

 

除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

属性名称 描述 示例
methodName 当前方法名 #root.methodName
method 当前方法 #root.method.name
target 当前被调用的对象 #root.target
targetClass 当前被调用的对象的class #root.targetClass
args 当前方法参数组成的数组 #root.args[0]
caches 当前被调用的方法使用的Cache #root.caches[0].name

 

当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:

@Cacheable(value={“users”, “xxx”}, key=”caches[1].name”)

public User find(User user) {

returnnull;

}

 

1.1.3  condition属性指定发生的条件

有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。

@Cacheable(value={“users”}, key=”#user.id”, condition=”#user.id%2==0″)

public User find(User user) {

System.out.println(“find user by user ” + user);

return user;

}

 

1.2     @CachePut

在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。

@CachePut(“users”)//每次都会执行方法,并将结果存入指定的缓存中

public User find(Integer id) {

returnnull;

}

 

1.3     @CacheEvict

@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

1.3.1  allEntries属性

allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

@CacheEvict(value=”users”, allEntries=true)

public void delete(Integer id) {

System.out.println(“delete user by id: ” + id);

}

 

1.3.2  beforeInvocation属性

清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

@CacheEvict(value=”users”, beforeInvocation=true)

public void delete(Integer id) {

System.out.println(“delete user by id: ” + id);

}

 

其实除了使用@CacheEvict清除缓存元素外,当我们使用Ehcache作为实现时,我们也可以配置Ehcache自身的驱除策略,其是通过Ehcache的配置文件来指定的。由于Ehcache不是本文描述的重点,这里就不多赘述了,想了解更多关于Ehcache的信息,请查看我关于Ehcache的专栏。

 

1.4     @Caching

@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。

@Caching(cacheable = @Cacheable(“users”), evict = { @CacheEvict(“cache2”),

@CacheEvict(value = “cache3”, allEntries = true) })

public User find(Integer id) {

returnnull;

}

 

1.5     使用自定义注解

Spring允许我们在配置可缓存的方法时使用自定义的注解,前提是自定义的注解上必须使用对应的注解进行标注。如我们有如下这么一个使用@Cacheable进行标注的自定义注解。

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Cacheable(value=”users”)

public @interface MyCacheable {

 

}

那么在我们需要缓存的方法上使用@MyCacheable进行标注也可以达到同样的效果。

@MyCacheable

public User findById(Integer id) {

System.out.println(“find user by id: ” + id);

User user = new User();

user.setId(id);

user.setName(“Name” + id);

return user;

}

 

2       配置Spring对Cache的支持

2.1     声明对Cache的支持

2.1.1  基于注解

配置Spring对基于注解的Cache的支持,首先我们需要在Spring的配置文件中引入cache命名空间,其次通过<cache:annotation-driven />就可以启用Spring对基于注解的Cache的支持。

<?xml version=“1.0” encoding=“UTF-8”?>

<beans xmlns=“http://www.springframework.org/schema/beans”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xmlns:cache=“http://www.springframework.org/schema/cache”

xsi:schemaLocation=“http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

     http://www.springframework.org/schema/cache

     http://www.springframework.org/schema/cache/spring-cache.xsd”>

 

<cache:annotation-driven/>

 

</beans>

 

<cache:annotation-driven/>有一个cache-manager属性用来指定当前所使用的CacheManager对应的bean的名称,默认是cacheManager,所以当我们的CacheManager的id为cacheManager时我们可以不指定该参数,否则就需要我们指定了。

<cache:annotation-driven/>还可以指定一个mode属性,可选值有proxy和aspectj。默认是使用proxy。当mode为proxy时,只有缓存方法在外部被调用的时候Spring Cache才会发生作用,这也就意味着如果一个缓存方法在其声明对象内部被调用时Spring Cache是不会发生作用的。而mode为aspectj时就不会有这种问题。另外使用proxy时,只有public方法上的@Cacheable等标注才会起作用,如果需要非public方法上的方法也可以使用Spring Cache时把mode设置为aspectj。

此外,<cache:annotation-driven/>还可以指定一个proxy-target-class属性,表示是否要代理class,默认为false。我们前面提到的@Cacheable、@cacheEvict等也可以标注在接口上,这对于基于接口的代理来说是没有什么问题的,但是需要注意的是当我们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操作的,定义在接口上的@Cacheable等Cache注解不会被识别到,那对应的Spring Cache也不会起作用了。

需要注意的是<cache:annotation-driven/>只会去寻找定义在同一个ApplicationContext下的@Cacheable等缓存注解。

 

2.1.2  基于XML配置

除了使用注解来声明对Cache的支持外,Spring还支持使用XML来声明对Cache的支持。这主要是通过类似于aop:advice的cache:advice来进行的。在cache命名空间下定义了一个cache:advice元素用来定义一个对于Cache的advice。其需要指定一个cache-manager属性,默认为cacheManager。cache:advice下面可以指定多个cache:caching元素,其有点类似于使用注解时的@Caching注解。cache:caching元素下又可以指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们类似于使用注解时的@Cacheable、@CachePut和@CacheEvict。下面来看一个示例:

<cache:advice id=“cacheAdvice” cache-manager=“cacheManager”>

<cache:caching cache=“users”>

<cache:cacheable method=“findById” key=“#p0”/>

<cache:cacheable method=“find” key=“#user.id”/>

<cache:cache-evict method=“deleteAll” all-entries=“true”/>

</cache:caching>

</cache:advice>

 

上面配置定义了一个名为cacheAdvice的cache:advice,其中指定了将缓存findById方法和find方法到名为users的缓存中。这里的方法还可以使用通配符“*”,比如“find*”表示任何以“find”开始的方法。

有了cache:advice之后,我们还需要引入aop命名空间,然后通过aop:config指定定义好的cacheAdvice要应用在哪些pointcut上。如:

<aop:config proxy-target-class=“false”>

<aop:advisor advice-ref=“cacheAdvice” pointcut=“execution(* com.xxx.UserService.*(..))”/>

</aop:config>

上面的配置表示在调用com.xxx.UserService中任意公共方法时将使用cacheAdvice对应的cache:advice来进行Spring Cache处理。更多关于Spring Aop的内容不在本文讨论范畴内。

 

2.2     配置CacheManager

CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为我们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另一种是基于第三方Cache实现——Ehcache,如果我们需要使用其它类型的缓存时,我们可以自己来实现Spring的CacheManager接口或AbstractCacheManager抽象类。下面分别来看看Spring已经为我们实现好了的两种CacheManager的配置示例。

2.2.1  基于ConcurrentMap的配置

<bean id=“cacheManager” class=“org.springframework.cache.support.SimpleCacheManager”>

<property name=“caches”>

<set>

<bean class=“org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean” p:name=“xxx”/>

</set>

</property>

</bean>

上面的配置使用的是一个SimpleCacheManager,其中包含一个名为“xxx”的ConcurrentMapCache。

 

2.2.2  基于Ehcache的配置

<!– Ehcache实现 –>

<bean id=“cacheManager” class=“org.springframework.cache.ehcache.EhCacheCacheManager” p:cache-manager-ref=“ehcacheManager”/>

<bean id=“ehcacheManager” class=“org.springframework.cache.ehcache.EhCacheManagerFactoryBean” p:config-location=“ehcache-spring.xml”/>

上面的配置使用了一个Spring提供的EhCacheCacheManager来生成一个Spring的CacheManager,其接收一个Ehcache的CacheManager,因为真正用来存入缓存数据的还是Ehcache。Ehcache的CacheManager是通过Spring提供的EhCacheManagerFactoryBean来生成的,其可以通过指定ehcache的配置文件位置来生成一个Ehcache的CacheManager。若未指定则将按照Ehcache的默认规则取classpath根路径下的ehcache.xml文件,若该文件也不存在,则获取Ehcache对应jar包中的ehcache-failsafe.xml文件作为配置文件。更多关于Ehcache的内容这里就不多说了,它不属于本文讨论的内容,欲了解更多关于Ehcache的内容可以参考我之前发布的Ehcache系列文章,也可以参考官方文档等。

 

3       键的生成策略

键的生成策略有两种,一种是默认策略,一种是自定义策略。

3.1     默认策略

默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:

n  如果方法没有参数,则使用0作为key。

n  如果只有一个参数的话则使用该参数作为key。

n  如果参数多余一个的话则使用所有参数的hashCode作为key。

 

如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。

使用基于注解的配置时是通过cache:annotation-driven指定的.

<cache:annotation-driven key-generator=“userKeyGenerator”/>

 

<bean id=“userKeyGenerator” class=“com.xxx.cache.UserKeyGenerator”/>

 

而使用基于XML配置时是通过cache:advice来指定的。

<cache:advice id=“cacheAdvice” cache-manager=“cacheManager” key-generator=“userKeyGenerator”>

</cache:advice>

 

需要注意的是此时我们所有的Cache使用的Key的默认生成策略都是同一个KeyGenerator。

3.2     自定义策略

自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

@Cacheable(value=”users”, key=”#id”)

public User find(Integer id) {

returnnull;

}

 

@Cacheable(value=”users”, key=”#p0″)

public User find(Integer id) {

returnnull;

}

 

@Cacheable(value=”users”, key=”#user.id”)

public User find(User user) {

returnnull;

}

@Cacheable(value=”users”, key=”#p0.id”)

public User find(User user) {

returnnull;

}

除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

属性名称 描述 示例
methodName 当前方法名 #root.methodName
method 当前方法 #root.method.name
target 当前被调用的对象 #root.target
targetClass 当前被调用的对象的class #root.targetClass
args 当前方法参数组成的数组 #root.args[0]
caches 当前被调用的方法使用的Cache #root.caches[0].name

 

当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:

@Cacheable(value={“users”, “xxx”}, key=”caches[1].name”)

public User find(User user) {

returnnull;

}

4       Spring单独使用Ehcache

前面介绍的内容是Spring内置的对Cache的支持,其实我们也可以通过Spring自己单独的使用Ehcache的CacheManager或Ehcache对象。通过在Application Context中配置EhCacheManagerFactoryBean和EhCacheFactoryBean,我们就可以把对应的EhCache的CacheManager和Ehcache对象注入到其它的Spring bean对象中进行使用。

 

4.1     EhCacheManagerFactoryBean

EhCacheManagerFactoryBean是Spring内置的一个可以产生Ehcache的CacheManager对象的FactoryBean。其可以通过属性configLocation指定用于创建CacheManager的Ehcache配置文件的路径,通常是ehcache.xml文件的路径。如果没有指定configLocation,则将使用默认位置的配置文件创建CacheManager,这是属于Ehcache自身的逻辑,即如果在classpath根路径下存在ehcache.xml文件,则直接使用该文件作为Ehcache的配置文件,否则将使用ehcache-xxx.jar中的ehcache-failsafe.xml文件作为配置文件来创建Ehcache的CacheManager。此外,如果不希望创建的CacheManager使用默认的名称(在ehcache.xml文件中定义的,或者是由CacheManager内部定义的),则可以通过cacheManagerName属性进行指定。下面是一个配置EhCacheManagerFactoryBean的示例。

<!– 定义CacheManager –>

<bean id=“cacheManager” class=“org.springframework.cache.ehcache.EhCacheManagerFactoryBean”>

<!– 指定配置文件的位置 –>

<property name=“configLocation” value=“/WEB-INF/config/ehcache.xml”/>

<!– 指定新建的CacheManager的名称 –>

<property name=“cacheManagerName” value=“cacheManagerName”/>

</bean>

4.2     EhCacheFactoryBean

EhCacheFactoryBean是用来产生Ehcache的Ehcache对象的FactoryBean。定义EhcacheFactoryBean时有两个很重要的属性我们可以来指定。一个是cacheManager属性,其可以指定将用来获取或创建Ehcache的CacheManager对象,若未指定则将通过CacheManager.create()获取或创建默认的CacheManager。另一个重要属性是cacheName,其表示当前EhCacheFactoryBean对应的是CacheManager中的哪一个Ehcache对象,若未指定默认使用beanName作为cacheName。若CacheManager中不存在对应cacheName的Ehcache对象,则将使用CacheManager创建一个名为cacheName的Cache对象。此外我们还可以通过EhCacheFactoryBean的timeToIdle、timeToLive等属性指定要创建的Cache的对应属性,注意这些属性只对CacheManager中不存在对应Cache时新建的Cache才起作用,对已经存在的Cache将不起作用,更多属性设置请参考Spring的API文档。此外还有几个属性是对不管是已经存在还是新创建的Cache都起作用的属性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking和cacheEventListeners,其中前四个默认都是false,最后一个表示为当前Cache指定CacheEventListener。下面是一个定义EhCacheFactoryBean的示例。

<!– 定义CacheManager –>

<bean id=“cacheManager” class=“org.springframework.cache.ehcache.EhCacheManagerFactoryBean”>

<!– 指定配置文件的位置 –>

<property name=“configLocation” value=“/WEB-INF/config/ehcache.xml”/>

<!– 指定新建的CacheManager的名称 –>

<property name=“cacheManagerName” value=“cacheManagerName”/>

</bean>

<!– 定义一个Ehcache –>

<bean id=“userCache” class=“org.springframework.cache.ehcache.EhCacheFactoryBean”>

<property name=“cacheName” value=“user”/>

<property name=“cacheManager” ref=“cacheManager”/>

</bean>

Tmux入门教程

WHY

使用Tmux有很多好处。我最看重的是,

  1. 远程服务器上持久地保存工作状态。比如,我在本地或服务器做某项工作,需要在Terminal打开几个窗口。每次换工作,还需要切换。用Tmux,便可以按照Session进行管理,每次轻松打开切换。
  2. 随时随地断开或连接Session。比如,在服务器上面运行一条命令,但是担心连接断开后命令终止,此时在tmux中运行命令,此时断网关机便问题不大,只需随后恢复Session便可以

Install

服务器上需要根据具体操作系统安装,包管理一般支持。若不支持,请参考GitHub – tmux/tmux: tmux source code。 Mac上只需如下命令:

brew install tmux
复制代码

Config

Tmux默认不支持鼠标滚动查看窗口中的前后内容。需要修改配置来支持。

touch ~/.tmux.conf

# 开启鼠标模式
set-option -g mouse on

# 允许鼠标选择窗格
# set -g mouse-select-pane on# 如果喜欢给窗口自定义命名,那么需要关闭窗口的自动命名
set-option -g allow-rename off
​
# 如果对 vim 比较熟悉,可以将 copy mode 的快捷键换成 vi 模式
set-window-option -g mode-keys vi
复制代码

如果对鼠标滚动效果依旧不满意,可以参考这篇blog:Better mouse scrolling in Tmux

Conception

tmux的主要元素分为三层:

  • Session 一组窗口的集合,通常用来概括同一个任务。session可以有自己的名字便于任务之间的切换。
  • Window 单个可见窗口。Windows有自己的编号,也可以认为和ITerm2中的Tab类似。
  • Pane 窗格,被划分成小块的窗口,类似于Vim中 C-w +v 后的效果。 一图以蔽之:
    conception.jpg

Session

Tmux为了防止与全局快捷键冲突,大部分快捷键需要先需要输入前缀Ctrl + b,下文用Prefix代替。

Session主要相关命令如下:

# 创建
tmux new # 不指定session name
tmux new -s [session-name]

# 删除Session
tmux kill-session -t [session-name]
tmux kill-server

# 列出当前Session
tmux ls # 
Prefix s # tmux 内

# 恢复Session
tmux a -t [session-name]
tmux a

# 断开Session
tmux detach
Prefix d

# 重命名Session
Prefix $
复制代码

Window

# 创建
Prefix c

# 选择窗口
Prefix + [number] # 选择第n个窗口
Prefix + p/n	# 前/后一个窗口

# 关闭窗口
Prefix &
exit

# 列出所有window(包含其他Session)
Prefix w 
j/k # 前后选择

# 搜索窗口
Prefix f

# 重命名当前窗口
Prefix ,
复制代码

Pane

# 创建
Prefix %	# 水平窗格
Prefix '"'	# 垂直窗格

# 关闭
Prefix x

# 切换
Prefix o # 在窗格间切换
Prefix q	# 显示窗格编号,输入编号切换

# 将当前窗格切换到新窗口
Prefix !

# 窗格交换位置
Prefix + {/}

作者:张伟杰
链接:https://juejin.im/post/5a8917336fb9a0633e51ddb9
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Gitbook 的使用和常用插件

Gitbook 是基于 Node.js 的命令行工具,用来创建漂亮的电子书,它使用 Markdown 或 AsciiDoc 语法来撰写内容,用 Git 进行版本控制,且可以托管在 Github 上。Gitbook 可以将作品编译成网站、 PDF、 ePub 和 MOBI 等多重格式。

如果你不擅长自己搭建 gitbook 环境,还可以使用 gitbook.com 在线服务来创建和托管你的作品,他们还提供了基于桌面的编辑器

如何使用

首先在全局安装 gitbook 客户端工具:

$ npm install gitbook-cli -g

然后在你的作品目录中创建两个必需的文件 README.md 和 SUMMARY.md,README.md 是作品的介绍,SUMMARY.md 是作品的目录结构,里面要包含一个章节标题和文件索引的列表:

# Summary

This is the summary of my book.

* [section 1](section1/README.md)
    * [example 1](section1/example1.md)
    * [example 2](section1/example2.md)
* [section 2](section2/README.md)
    * [example 1](section2/example1.md)

根据 SUMMARY.md 的目录结构初始化各个章节文件:

$ gitbook init

运行服务,在编辑内容后实时预览:

$ gitbook serve

服务器启动后,浏览器打开 http://localhost:4000 查看,撰写完后可以生成静态网站用来发布:

$ gitbook build

使用插件

Gitbook 本身功能丰富,但同时可以使用插件来进行个性化定制。Gitbook 插件 里已经有100多个插件,可以在 book.json 文件的 plugins 和 pluginsConfig 字段添加插件及相关配置,添加后别忘了进行安装。

// book.json
{
  "title": "Webpack 中文指南",
  "description": "Webpack 是当下最热门的前端资源模块化管理和打包工具,本书大部分内容翻译自 Webpack 官网。",
  "language": "zh",
  "plugins": [
    "disqus",
    "github",
    "editlink",
    "prism",
    "-highlight",
    "baidu",
    "splitter",
    "sitemap"
  ],
  "pluginsConfig": {
    "disqus": {
      "shortName": "webpack-handbook"
    },
    "github": {
      "url": "https://github.com/zhaoda/webpack-handbook"
    },
    "editlink": {
      "base": "https://github.com/zhaoda/webpack-handbook/blob/master/content",
      "label": "编辑本页"
    },
    "baidu": {
        "token": "a9787f0ab45d5e237bab522431d0a7ec"
    },
    "sitemap": {
        "hostname": "http://zhaoda.net/"
    }
  }
}
# 安装插件
$ gitbook install ./

常用插件

内容顶部显示 编辑本页 链接。

在每个页面顶部和底部添加广告或任何自定义内容。

splitter

在左侧目录和右侧内容之间添加一个可以拖拽的栏,用来调整两边的宽度。

image-captions

抓取内容中图片的 alt 或 title 属性,在图片下面显示标题。

github

在右上角显示 github 仓库的图标链接。

anchors

标题带有 github 样式的锚点。

chart

使用 C3.js 图表。

styles-sass

使用 SASS 替换 CSS。

styles-less

使用 LESS 替换 CSS。

ga

添加 Google 统计代码。

disqus

添加 disqus 评论插件。

sitemap

生成站点地图。

latex-codecogs

使用数学方程式。

mermaid

使用流程图。

book-summary-scroll-position-saver

自动保存左侧目录区域导航条的位置。

sharing

默认的分享插件。

fontsettings

默认的字体、字号、颜色设置插件。

默认搜索插件。

自定义页脚,显示版权和最后修订时间。

prism

基于 Prism 的代码高亮。

atoc

插入 TOC 目录。

ace

插入代码高亮编辑器。

highlight

默认的代码高亮插件,通常会使用 prism 来替换。

github-buttons

显示 github 仓库的 star 和 fork 按钮。

sectionx

分离各个段落,并提供一个展开收起的按钮。

mcqx

使用选择题。

include-codeblock

通过引用文件插入代码。

fbqx

使用填空题。

spoiler

隐藏答案,当鼠标划过时才显示。

anchor-navigation

锚点导航。

youtubex

插入 YouTube 视频。

redirect

页面跳转。

expandable-chapters

收起或展开章节目录中的父节点。

baidu

使用百度统计。

duoshuo

使用多说评论。

jsfiddle

插入 JSFiddle 组件。

jsbin

插入 JSBin 组件。

开发插件

最好先查看别人的插件是怎么做的,然后再看官方文档

在NAT下tcp_tw_recycle参数调整

一,故障描述:

从昨天开始,在值班群中陆续值班人员反映系统后台存在卡顿问题,如下图:
记一次由tcp_tw_recycle参数引发的血案
而且在卡顿的同时登陆服务器也会卡好久。此现象只在一台服务器有出现。

二,故障分析:

1,登陆服务器查看资源使用top,vmstat等命令查看了一番发现服务器各项指标都没有异常。于是将问题转向了网络层。
2,客户端端值班人员反映只有在访问系统后台的时候才会出现卡顿,访问其他网站正常。
3,本地使用ping服务器外网ip正常返回,无丢包,延迟也正常。
4,使用http-ping工具时,问题出现了,会经常性的出现连接失败:
(http-ping工具下载地址https://www.softpedia.com/get/Network-Tools/IP-Tools/http-ping.shtml)
记一次由tcp_tw_recycle参数引发的血案
为什么会连接失败呢?
5,使用tcpdump抓包在卡顿的时候会抓到大量的syn请求,但服务器没有响应:
记一次由tcp_tw_recycle参数引发的血案
6,登录服务器查看tcp相关数据

$ netstat -s | grep -i listen
    326 times the listen queue of a socket overflowed
    751346 SYNs to LISTEN sockets dropped

发现在卡顿时有大量tcp syn包被丢弃,数值一直在增长。

三,故障处理:

在查阅资料并结合实际情况后,发现该服务器同时启用了 tcp_timestamps和tcp_tw_recycle参数。
后想起,之前同事为改善time_wait连接数过多问题曾改过该内核参数。
解决办法是,关闭tcp_tw_recycle:

$ vi  /etc/sysctl.conf
#修改为如下
net.ipv4.tcp_tw_recycle = 0
#保存退出,使之生效
$ sysctl -p

再观察,发现服务已正常,卡顿现象消失。

四,总结:

我们先来man一下这两个参数(man tcp):

 tcp_timestamps (Boolean; default: enabled; since Linux 2.2)
              Enable RFC 1323 TCP timestamps.

tcp_timestamp 是 RFC1323 定义的优化选项,主要用于 TCP 连接中 RTT(Round Trip Time) 的计算,开启 tcp_timestamp 有利于系统计算更加准确的 RTT,也就有利于 TCP 性能的提升。(默认开启)
关于tcp_timestamps详情请见:https://tools.ietf.org/pdf/rfc7323.pdf

 tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)
              Enable fast recycling of TIME_WAIT sockets.  Enabling this option is not recommended since this causes problems when working with NAT (Network Address
              Translation).

开启tcp_tw_recycle会启用tcp time_wait的快速回收,这个参数不建议在NAT环境中启用,它会引起相关问题。

tcp_tw_recycle是依赖tcp_timestamps参数的,在一般网络环境中,可能不会有问题,但是在NAT环境中,问题就来了。比如我遇到的这个情况,办公室的外网地址只有一个,所有人访问后台都会通过路由器做SNAT将内网地址映射为公网IP,由于服务端和客户端都启用了tcp_timestamps,因此TCP头部中增加时间戳信息,而在服务器看来,同一客户端的时间戳必然是线性增长的,但是,由于我的客户端网络环境是NAT,因此每台主机的时间戳都是有差异的,在启用tcp_tw_recycle后,一旦有客户端断开连接,服务器可能就会丢弃那些时间戳较小的客户端的SYN包,这也就导致了网站访问极不稳定。

主机A SIP:P1 (时间戳T0) —> Server 主机A断开后
主机B SIP:P1 (时间戳T2) T2 < T0 —> Server 丢弃