0%

uptime

1
2
$uptime
07:13:53up8days,19min, 1user, load average: 1.98,2.15,2.21

load average: 1.98,2.15,2.21 一分钟内的负载,5分钟内的负载,15分钟内的负载。

为了进一步理解系统负载,需要做一些假设。假设系统负载如下:
23:16:49 up 10:49, 5 user, load average: 1.00, 0.40, 3.35
在单核系统中意味着:

  • CPU 被充分利用(100%);最近的 1 分钟有 1 个进程在运行。
  • CPU 有 60% 处于空闲状态;在最近的 5 分钟没有进程等待 CPU 时间。
  • CPU 平均过载了 235%;最近的 15 分钟平均有 2.35 个进程在等待 CPU 时间。
    在双核系统中意味着:
  • 有一个 CPU 处于完全空闲状态,另一个 CPU 被使用;最近的 1 分钟没有进程等待 CPU 时间。
  • CPU 平均 160% 处于空闲状态;最近的 5 分钟没有进程等待 CPU 时间。
  • CPU 平均过载了 135%;最近的 15 分钟有 1.35 个进程等待 CPU 时间。

top -H -p pid

cpu 负载监控,实时查看占用cpu高的线程

vmstat

系统负载监控

iostat

磁盘利用率监控

netstat -anp | grep ‘ip/prot’

查看网络连接信息

lsof -p pid

  • 使用lsof -p pid 命令查看进程打开的文件,如果大部分文件都是同一类型的文件,说明可能未关闭文件流。找到打开文件的代码,关闭文件流即可。
  • 如果不存在未关闭文件流的问题,且业务本身就需要处理大量文件,则修改 /etc/security/limits.conf 文件如下内容;
    1
    2
    * hard nproc 10240
    * soft nproc 10240

压测分析步骤

  1. 优化服务器本省的TCP信息
  • tcp 端口的释放时间
  1. 查看内存,cpu,硬盘的IO
  • 内存 Free -h 查看 buffers/cache 的值,buffers/cache 表示实际占用的值
  • iostat
  • top
  1. 分析服务器当前的并发数,文件打开数

netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

Liunx 内核优化
vim /etc/security/limits.conf

  •           soft    nproc           10240
    
  •           hard    nproc           16384
    
  •           soft    nofile          60240
    
  •           hard    nofile          65536
    
  •           hard    core           unlimited
    

vim /etc/security/limits.d/90-nproc.conf

  •      soft    nproc     50240
    

root soft nproc unlimited

vim /etc/sysctl.conf

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 60

net.ipv4.tcp_keepalive_time = 600
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000

net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_timestsmps = 0

net.ipv4.tcp_max_syn_backlog = 65536
net.core.netdev_max_backlog = 32768
net.core.somaxconn = 32768

net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_wmem = 8192 436600 873200
net.ipv4.tcp_rmem = 32768 436600 873200
net.ipv4.tcp_mem = 94500000 91500000 92700000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_fin_timeout = 30

Liunx 最大文件数

命令:ulimit -a
open files (-n) 1024
“open files” 参数选项后面的数值就是当前系统的支持的最大打开文件数

Kafka LogStash ES

java 程序日志通过 logback,将日志写入kafka,在通过logstash采集kafka中数据,将数据写入ES中,最后通过 Kibana 查询程序日志。

中文编码

问题: 程序日志中文写入 kafka,最后在Kibana中以Unicode的方式显示。

解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
input {
kafka {
bootstrap_servers => "kakfa ip"
group_id => "Cosumer_10-127-28-11"
topics => "logstash"
<!-- codec => json_lines 修改中文显示方式 -->
codec => json_lines
}
}

filter {

}

output {
elasticsearch{
action => "index"
hosts => ["es ip"]
user => "elastic"
password => "changeme"
index => "applog"
}
}

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.11</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>com.github.danielwegener</groupId>
<artifactId>logback-kafka-appender</artifactId>
<version>0.2.0-RC1</version>
</dependency>

logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

<?xml version="1.0" encoding="UTF-8"?>
<!--该日志将日志级别不同的log信息保存到不同的文件中 -->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>

<springProperty scope="context" name="springAppName"
source="spring.application.name"/>

<springProperty scope="context" name="springAppPort"
source="server.port"/>

<appender name="KafkaAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeContext>true</includeContext>
<includeCallerData>true</includeCallerData>
<customFields>{"system":"project-name"}</customFields>
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>
<topic>logstash</topic>
<keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy"/>
<deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy"/>
<producerConfig>bootstrap.servers=</producerConfig>
</appender>

<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="KafkaAppender"/>
</root>
</configuration>

Spring Cloud Config

application.yml 和 bootstrap.yml 区别

加载顺序

  • bootstrap.yml 先加载
  • application.yml 后加载

bootstrap.yml 用于应用程序上下文的引导阶段。
bootstrap.yml 由父 Spring ApplicationContext 加载。
父ApplicationContext被加载到使用application.yml 的之前

配置区别

bootstrap.yml 和 application.yml 都可以用来配置参数

  • bootstrap.yml 可以理解为系统级别的一些参数配置,这些参数一般是不会变动的。
  • application.yml 可以用来定义应用节级别的,如果搭配 spring-cloud-config 使用application.yml 里面定义的文件可以实现动态替换。
    使用Stpring Cloud Config Server时,应在bootstrap.yml 中指定:
  • spring.application.name
  • spring.cloud.config.server.git.url
  • 一些加密/解密信息

application 的加载原理

启动上下文

Spring Cloud 会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。初始化的时候,Bootstrap Context 复制从外部源加载配置属性并解析配置。 这两个上下文共享一个从外部获取的Environment.Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap contextApplication Context 有着不同的约定,所有新增了一个bootstrap.yml 文件,而不是使用application.yml。保证Bootstarp ContextApplication Context 配置的分离。

应用上下文层次结构

如果你通过 SpringApplicaion 或者 SpringApplicationBuilder创建一个Application Context,那么会为spring应用的Application Context 创建父上下文Bootstrap Context。在Spring里有个特性,子上下文会继承父类的property sources,会新增额外的property sources。额外的property sources 有:

  • “bootstrap”: 如果在Boostrap Context 扫描到PropertySourceLocator并且有属性,则会添加到CompositePropertySource.Spring Cloud COnfig 就是通过这种方式来添加属性,详情请查看源码 ConfigServicePropertySourceLocator
  • “applicationConfig:[classpath:bootstrap.yml]” (如果有 spring.profiles.active=production,则例如 applicationConfig:[classpath:/bootstrap.yml]#product): 如果你是用bootstrap.yml 来配置Bootstrap Context,他比application.yml 优先级别要低,它将添加到子上下文,作为Spring Boot 应用程序的一部分。

由于优先级规则,Bootstrap Context 不包含从Bootstrap.yml 来的数据,但可以用它作为默认设置。
你可以很容易的扩展任何你建立的上下文层次,可以使用它提供的接口,或者使用 SpringApplicationBuillder包含的方法(parent(),child(),sibling())。 Bootstrap Context 将是最高级别的父类。扩展的每一个Context都有自己的 bootstrap property source(有可能是空的)。扩展的每一个Context 都有不同 spring.application.name。 同一层层次的父子上下文原则上也有着不同的名称,因此,也会有不同的Config Server配置。子上下文的属性在相同名字的情况下将覆盖父上下文的属性。
注意:SpringApplicationBuilder允许共享Environment到所有层次,但是不是默认的。 因此,同级的兄弟上下文不在和父类共享一些东西的时候不一定有相同的profiles或者property sources。

修改Bootstrap属性配置

源码位置: BootstrapApplicationLister

1
2
3
4
5
6
String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String, Object> bootstrapMap = new HashMap<>();bootstrapMap.put("spring.config.name",configName);
if(StringUtils.hasText(configLocation)){
bootstrapMap.put("spring.config.location", configLocation);
}

bootstrap.yml 是有spring.cloud.bootstrap.name(默认:“bootstrap”) 或者 spring.cloud.bootstrap.location(默认空)。这些属性行为与spring.config.* 类似,通过它的Environment来配置引导ApplicationContext。如果有一个激活的profile(来源与 spring.profiles.active 或者 Environment 的Api构建),例如 bootstrap-development.properies 就是配置了 profile为development的配置文件。

覆盖远程属性

property sources 被bootstrap context添加到应用通常远程的方式,比如“Config Server”。默认情况下,本地的配置文件不能覆盖远程配置,但是可以通过启动命令参数来覆盖远程配置。如果需要本地文件覆盖远程文件,需要在远程配置文件里设置 spring.cloud.config.allowOverride=true(这个配置不能在本地被设置)。一旦设置了这个权限,你可以配置更加细粒度的配置来配置覆盖的方式。

1
2
spring.cloud.config.overrideNoe = true #覆盖任何本地属性
spring.cloud.config.overrideSystemProperties=false #仅仅系统属性和环境变量

源文件详见 PropertySourceBootstrapProperties

自定义启动配置

bootstrap context 是依赖 /META-INF/spring.factories 文件里面的 org.springframework.cloud.bootstrap.BootstrapConfiguration 条目下面,通过逗号分隔的Spring @Configuration 类来建立的配置。任何main application context 需要自动注入的Bean可以在这里通过这种方式获取。这也是 ApplicationContextInitializer 建立@Bean的方式。可以通过 @Order来更改初始化序列,默认是”last”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# spring-cloud-context-1.1.1.RELEASE.jar
# spring.factories
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.context.restart.RestartListener

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration

你添加的自定义BootstrapConfiguration类没有错误的@ComponentScanned到你的主应用上下文,他们可能是不需要的。使用一个另外的包不被@ComponentScan或者@SpringBootApplication注解覆盖到。
bootstrap context 通过 spring.factories 的配置类初始化所有的Bean都会在SpringApplication启动前加入到它的上下文里去。

自定义引导配置来源: Bootstrap Property Sources

默认的property source 添加额外的配置是通过配置服务(Config Server),你也可以自定义添加 property soruce 通过实现 PropertySourceLocator 接口来添加。你可以使用它加配置属从不同的服务,数据库或者其他。
demo:

1
2
3
4
5
6
7
8
@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}

Environment 被 ApplicationContext建立,并传入 property sources(可能不同的profile有不同的属性),所以,你可以从Environment寻找找一些特别的属性。比如spring.application.name,它是默认的Config Server property source。
如果你建立了一个jar包,里面添加了一个 META-INF/spring.factories文件:org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator 。那么“CostomProperty” 的

参考资料

(SpringCloud入门之常用的配置文件 application.yml和 bootstrap.yml区别)[https://www.cnblogs.com/BlogNetSpace/p/8469033.html]

(Spring Boot)[https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/]

(Spring Boot)[https://github.com/spring-projects/spring-boot]

Spring Aop:
Spring 集成了AspectJ做为Aop的一个特定实现,同时还在JVM动态代理/CGLIB的基础上,实现了一个AOP框架,作为Spring集成到其他模块的工具,比如 TranscationProxyFactoryBean 声明式事务处理,就是通过AOP集成到Spring中的。在这个模块中,Spring AOP 实现了一个完整的建立AOP代理对象,实现AOP拦截器,直至实现各种Advice通知的过程。

Spring 中提供了很多存储器,例如: SimpleCacheManager , EhCacheCacheManager, GuavaCacheManager, CompositeCacheManger。处理核心的Spring框架之外,Spring Date 提供提供了Redis缓存管理器。RedisCacheManager。

Spring Boot 中通过 @EnableCaching 注解自动化配置合适的缓存管理器。
默认情况下Spring Boot 根据以下顺序自动检测缓存提供者:

  • Generic
  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • Infinispan
  • Redis
  • Guava
  • Simple

若我们手动配置RedisTemplate后,Spring Boot 就无法自动给RedisCacheManager设置redisTemplate,所以需要自己配置RedisCacheManager。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
RedisConfig.java

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.afterPropertiesSet();
setSerializer(redisTemplate);
return redisTemplate;
}

private void setSerializer(RedisTemplate<String, String> template) {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
}

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
// 设置缓存过期时间,秒
rcm.setDefaultExpiration(60);
return rcm;
}
}

Spring 提供了以下注解来声明缓存规则

@Cacheable triggers cache population 表明Spring在调用方法之前,首先应该在缓存中查找方法的返回值,,如果这个值能够找到,就会返回这个值。否则的话,这个方法就会被调用,返回值会放到缓存中
@CacheEvict triggers cache eviction 表明Spring应该在缓存中清除一个或多个条目
@CachePut updates the cache without interfering 表明Spring应该将方法的返回值放到缓存中,在方法的调用前并不会检查,方法始终都会被调用
@Caching regroups multiple cache operations to be applied on a method
@CacheConfig shares some common cache-related setting at class-level 可以在类层级配置一些公用的缓存配置

@Cacheable 和 @CachePut 共有属性:

属性 类型 描述
value String[] 要使用的缓存名称
condition String SpEL 表达式,如果得到的值是false的话,不会将缓存应用到方法调用上
key String SpEL表达式,用来计算自定义的缓存key
unless String SpEL表达式,如果得到的值是true的话,返回值不会放到缓存之中

使用 CacheManager 的有点:
CacheManager 是所有缓存管理器的父级,定义了一些通用接口。
具体的缓存实现可以继承CacheManger,并自行实现功能。通过这个方法可以进行拆分解耦,当替换其他缓存插件时,不需要修改代码,只需要修改配置文件就行。

Redis 在项目中的使用

  1. 开启Redis事务 @TransactionalstringRedisTemplate.setEnableTransactionSupport(true); 需要同时启用。
  2. 查询不存在的数据,需要将一组空数据写入Redis,防止数据库被穿透。
  3. 查询数据时,需要使用 lock ,来防止所有请求都走数据库,而不走Redis,大流量,大并发的情况下。

Redis 集群

Redis 集群只有 db0,不支持 select db操作,不支持 Redis 事务操作。

代码分析

命名方法

  • 定义变量名字,使用 名词而非东西
  • 基于Rest 风格,使用 名词复数 + Http动词定义url,实现功能

编码标准

  1. checkStyle
  2. 阿里编码标准

代码重复(PMD的CPD的使用)

测试代码覆盖率(Eclemma)

依赖项分析(JDepend的使用)

复杂度分析(Metrics的使用)

参考资料

Elastic Stack 入门教程

ElasticSearch 与 Kibana

Beats 轻量型数据采集器

  1. Filebeat
  2. Packetbeat

Logstash

处理流程
Input => Filter => Output
File grok stdout
Redis mutate elastisearch
Beats drop redis
Kafka date kafka

使用 logstash 收集 nginx log 数据

ElasticSearch

Packetbeat 监听端口,采集log

logstash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
input {
beats: {prot => 5044}
}

filter {
if "search" in [request]{
grok {match => {}}
grok {match => {}}
}
}

output {
if "search" in [request]{

}
}

packetbeat

1