Earyant的技术博客

欢迎来到Earyant的技术博客,在这里我将与你分享新技术。

sed编辑器

不修改原内容的行级别流编辑器

  • sed ‘s/baidu /earyant/‘ access.log | head -10

    将日志文件中的baidu替换成earyant输出
    s表示执行的事文本替换命令

  • sed -n ‘2,6p’ access.log

    -n表示只输出指定的行,’2,6p’表示选择的事第二行到第六行

  • sed ‘/earyant/d’ access.log

    d表示执行文本删除命令,将包含earyant的行删除

  • sed ‘=’ access.log

    显示文本行号

  • sed -e ‘i\head’ access.log | head -10

    在行首插入文本

  • sed -e ‘a\end’ access.log | head -10

    在文末追加文本

  • sed -e ‘/baidu/c\hello’ access.log | head -10

    c命令对文本进行替换,查找/baidu/匹配的行,用hello对匹配的行进行替换,与s不同的是,这个替换行,s是替换单词

  • sed -n ‘1,5p;1,5=’ access.log

    多条命令,分号隔开

awk

  • awk ‘{print $1}’ access.log | head -10

    打印指定的列

  • awk ‘/baidu/{print $5,%6}’ access.log | head -10

    筛选指定的行,并且打印出其中一部分列

  • awk ‘length{$0} > 40 {print $3}’ access.log | head -10

    $0 表示当前的行,length($0)获取当前行的长度,print $3 打印出第三列

  • awk ‘{line = sprintf (“method:%s,response:%s”,$3,$7); print line}’ access.log | head -10

    定义line接收sprintf输出,sprintf用户格式化输出第三行的请求式和第七行的响应时间

shell

  • 系统load超过2或者磁盘利用率超过85%报警:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
#earyantlee@gmail.com

#top取系统load值, -n 1 表示只刷新一次
# sed 过滤第一行
# top命令会输出1分钟、5分钟、15分钟load的平均值,awk筛选出1分钟内的平均load,赋值给load
load = `top -n 1| sed -n '1p' | awk '{print $11}'`
# 从右边开始,过滤掉不需要的逗号
load = ${load%\,*}
# df取得磁盘利用率信息,用sed筛选磁盘总利用率第二行
disk_usage = `df -h | sed -n '2p' |awk '{print $(NF - 1)}' `
# 过滤掉百分号
disk_usage = ${disk_usage%\%*}
overhead = `expr $load \> 2.00`
if [$overhead -eq 1];then
echo "System load is overhead"
fi
if [$disk_usage -gt 85 ]; then
echo "disk is nearly full ,need more disk space"
fi
exit 0
  • 读取日志文件,对字段切割,插入到sql。
    db:
1
2
3
4
5
6
7
8
9
create table access_log(
ip varchar(20),# ip地址
rt bigint,# 响应时间
method varchar(10), #请求方式
url varchar (400), # 请求地址
refer varchar(400),# 请求来源
return_code int,# 返回码
response_size bigint # 响应大小
);

shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
#earyantlee@gmail.com
# 日志路径
ACCESS_FILE="D:\\0\\nginx-1.11.13\\logs\\access2.log"
while read LINE
do
# 系统数组分隔符
OLD_IFS="$IFS"
IFS=" "
# 每行分割成数组
field_arr=($LINE)
IFS="$OLD_IFS"
# 生成插入语句,各列的值对应什么,懒得改了。
STATEMENT="insert into access_log values('${field_arr[0]}' , '${field_arr[1]}', '${field_arr[2]}', '${field_arr[3]}', '${field_arr[4]}', '${field_arr[5]}', '${field_arr[6]}');"
echo $STATEMENT
mysql earyant -u root -p123456 -e "${STATEMENT}"
done < $ACCESS_FILE
exit 0

查看文件的内容

  • cat:正序查看

    cat access.log

  • tac:倒序查看

    tac access.log

分页显示文件

  • more

    more access.log

    Enter显示文件下一行
    空格显示下一页
    F显示下一屏内容
    B显示上一屏内容

  • less

    /GET 查找GET字符串

显示文件尾

  • tail

    tail -f -n 500 access.log

    -f 持续查看
    -n 显示最后n行

显示文件头

  • head

    head -n2 access.log

内容排序

  • sort

    sort -n -r access.log

    -n 按照数字排序
    -r 逆序排序

    sort -k 2 -t “ “ -n access.log

    -k 指定排序的列
    -t 指定列分隔符
    -n 按照数字排序

字符统计

  • wc

    wc -l access.log

    -l 统计文件中的行数

    wc -c access.log

    -c 显示文件的字节数

    wc -L access.log

    -L得出最长的行长度

    wc -w access.log

    -w 查看文件包含多少单词

重复行

  • uniq

    sort uniqfile| uniq -c

    -c 用来在每一行前面加上该行出现的次数

    sort uniqfile | uniq -c -u

    -u 只会显示出现一次的行

    sort uniqfile | uniq -c -d

    -d 只会显示重复出现的行

字符串查找

  • grep

    >   grep earyant access.log
    
      earyant 为指定的查找串
    
    > grep -c earyant access.log
    
      -c 可以显示查找到的行数
    
    >   grep 'G.\*T' access.log
    
      支持正则表达式
    

    文件查找

  • find

    find /home/earyant -name access.log

    在/home/earyant 目录下查找文件名为access.log的文件

    find /home/earyant -name “*.txt”

    find . -print

    打印当前目录所有文件

  • whereis

    whereis java

  • which

    which java

表达式求值

  • expr

    expr 10 * 3
    expr 10 % 3
    expr index “earyant.github.io” earyant

压缩

  • tar

    tar -cf aaa.tar tmp1 tmp2

    将当前目录下的tmp1和tmp2目录打包成aaa.tar
    -c 表示生成新包
    -f 指定包名称
    

    tar -tf aaa.tar

    -t 能够列出包中文件的名称
    

    tar -xf aaa.tar

    -x 将打包文件解压
    

url 访问工具

  • curl

    curl www.baidu.com

    curl -i www.baidu.com

    -i 返回带header的文档

    curl -I www.baidu.com

    -I 只返回页面的header信息

    查看请求访问量

cat access.log | cut -f1 -d “ “ | sort | uniq -c | sort -k 1 -n -r | head -10

访问量排名前10的ip地址

cat access.log | cut -f4 -d “ “ | sort | uniq -c | sort -k 1 -n -r | head -10

页面访问量排名前10的url

查看最耗时的页面

cat access.log | sort -k 2 -n -r |head -10

统计404请求的占比

1
export total_line = `wc -l access.log | cut -f1 -d " "` && export not_found_line = `awk '$6=='404'{print $6}' access.log | wc -l`  && expr $ not_found_line \*100 / $total_line

1.1-使用和避免null

map允许null作为键,但只能有一个。
concurrentHashMap不允许null作为键。

Optional

1
2
3
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

静态创建方法

  • of
  • ofNullable
  • empty

    实例方法

  • isPresent
  • get
  • or (jdk中是orElse)
  • orNull
  • asSet

意义:
使用Optional除了赋予null语义,增加了可读性,最大的优点在于它是一种傻瓜式的防护。Optional迫使你积极思考引用缺失的情况,因为你必须显式地从Optional获取引用。直接使用null很容易让人忘掉某些情形,尽管FindBugs可以帮助查找null相关的问题,但是我们还是认为它并不能准确地定位问题根源。
如同输入参数,方法的返回值也可能是null。和其他人一样,你绝对很可能会忘记别人写的方法method(a,b)会返回一个null,就好像当你实现method(a,b)时,也很可能忘记输入参数a可以为null。将方法的返回类型指定为Optional,也可以迫使调用者思考返回的引用缺失的情形。

使用方式:

  • 错误的使用方式:
    1
    2
    3
    4
    5
    6
    Optional<User> user = Optional.ofNullable(user);
    if (user.isPresent()) {
    int sex = user.getSex();
    // 链式调用,最容易出现空指针
    int age = user.getParent().getParent().getParent().getAge();
    }
  • 正确的使用方式
    1
    2
    3
    Optional<User> user = Optional.ofNullable(user);
    int sex = user.map(User::getSex).orElse(0);
    int age = user.map(User::getParent).map(User::getParent).map(User::getParent).map(User::getAge).orElse(0);

其他处理null的便利方法

  • Objects.firstNonNull(T, T)
  • Objects还有其它一些方法专门处理null或空字符串:emptyToNull(String),nullToEmpty(String),isNullOrEmpty(String)。

1.2-前置条件 Preconditions

方法声明(不包括额外参数) 描述 检查失败时抛出的异常
checkArgument(boolean) 检查boolean是否为true,用来检查传递给方法的参数。 IllegalArgumentException
checkNotNull(T) 检查value是否为null,该方法直接返回value,因此可以内嵌使用checkNotNull。 NullPointerException
checkState(boolean) 用来检查对象的某些状态。 IllegalStateException
checkElementIndex(int index, int size) 检查index作为索引值对某个列表、字符串或数组是否有效 IndexOutOfBoundsException
checkPositionIndex(int index, int size) 检查index作为位置值对某个列表、字符串或数组是否有效。 IndexOutOfBoundsException
checkPositionIndexes(int start, int end, int size) 检查[start, end]表示的位置范围对某个列表、字符串或数组是否有效* IndexOutOfBoundsException

参考

参考

如果我孑身一人是个赛车手
无牵无挂,无畏无惧
即使受伤、死亡
也会为我爱的事倾尽所有

如果我生在乱世是个将军
交出我爱的人换取一时和平
不要和我讲大义
提枪上马,马革裹尸不休

最甜的是西瓜中间的那口

可我连籽都吃掉

我爱的东西本来就不多

爱就爱它的所有

本文记录docker搭建elk,并简单结合logstash日志输出

filebeat

docker pull docker.elastic.co/beats/filebeat:6.2.1

  • filebeat.yml
    enabled要改为true,filebeat默认为false
    1
    2
    3
    4
    5
    6
    7
    8
    filebeat.prospectors:
    - type: log
    enabled: true
    paths:
    - D:\workspace\baidu\baiyi\baidu\dsp\dsp-main-server\log\*.log
    output:
    logstash:
    hosts: ["192.168.99.100:5044"]

    docker run -it —name filebeat -v /data/filebeat.yml::/usr/share/filebeat/filebeat.yml

logstash

改写/opt/logstash/logstash/config/logstash.yml文件

  • logstash.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
input{
beats {
port => 5044
}
}
output{
stdout{ codec => rubydebug }
elasticsearch {
hosts => "192.168.99.100:9200"
index => "t-server-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
}

本文章记录自己通过docker搭建elk的过程

docker下载镜像

1
docker pull sebp/elk :latest

docker 至少分配3g内存

elasticsearch 至少需要2g,logstash至少需要1g

设置最大运行线程数

1
最大线程数要超过262144
  • 使用docker toolbox方式 先进入虚拟机

    docker-machine ssh default

  • 永久生效配置

    sudo vi /etc/sysctl.conf

    vm.max_map_count=262144

    sudo sysctl -p //生效

  • 临时生效配置

    sysctl -w vm.max_map_count=262144

启动

启动,并配置端口映射

docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -it —name elk sebp/elk

查看kibana

打开网址 http:\192.168.99.100:5601 若打开成功说明部署成功

配置使用

  1. 进入容器

    docker exec -it elk /bin/bash

  2. 配置logstash

    /opt/logstash/bin/logstash -e ‘input { stdin { } } output { elasticsearch { hosts => [“localhost”] } }’

    注意:如果看到这样的报错信息 Logstash could not be started because there is already another instance using the configured data directory. If you wish to run multiple instances, you must change the “path.data” setting. 请执行命令:service logstash stop 然后在执行就可以了。

    当命令成功被执行后,看到:Successfully started Logstash API endpoint {:port=>9600} 信息后,输入:test 然后回车,模拟一条日志进行测试。

    1. 打开浏览器,输入:http://192.168.99.100:9200/_search?pretty ,就会看到我们刚刚输入的日志内容
    2. 创建kibana与logstash关联
    3. index name or pattern 输入 logstash-*
    4. time-field name 输入 @timestamp
    5. create
    6. 打开kibana首页即可看到刚刚输入的内容

java8新特性

lambda表达式

java8接受一个函数作为另一个函数的参数传入进去

Arrays.asList( “a”, “b”, “d” ).forEach( e -> System.out.println( e ) ); 参数的类型是编译器推断出来的

Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更高)。

默认方法和静态方法

在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。这方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……

重复注解

Java编译器的新特性

1
2
3
4
5
6
7
8
public class ParameterNames {
public static void main(String[] args) throws Exception {
Method method = ParameterNames.class.getMethod( "main", String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( "Parameter: " + parameter.getName() );
}
}
}

Parameter: args

对于有经验的Maven用户,通过maven-compiler-plugin的配置可以将-parameters参数添加到编译器中去。

1
2
3
4
5
6
7
8
9
10
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

Java 类库的新特性

Optional

平时用的判空:

1
2
3
4
5
6
7
public void get(String name){
if (name == null){
return "";
}else{
return name;
}
}

改成Optional:

1
2
3
4
5
6
7
8
public void get(String name){
Optional<String > names = Opntional.of(name);
if (names.isPresent()){
return names.get();
}else{
return "";
}
}

如果单纯改成这样,还不如直接写成 ==null,看起来更加繁琐。 应该改成这样:

1
2
3
4
public void get(String name){
return Opntional.of(name).get().orElse("");

}

Stream

1
2
3
4
5
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();

filter

对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;

map

映射转换

mapTo*

映射转换成 的类型, 包括Integer、Double、Long

flatMap

合并集合,和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;

1
2
3
{{1,2},{3,4},{5,6}} - > flatMap - > {1,2,3,4,5,6}

{'a','b'},{'c','d'},{'e','f'}} - > flatMap - > {'a','b','c' D", 'E', 'F'}

flatMapTo*

distinct

对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;

sorted

peek

监控器

提供一个Consumer消费函数,返回一个新的Stream,如果只写 Stream.of(“1”,”2”,”3”).peek(e-> System.out.Println(e)) 是不会打印结果的 新Stream每个元素被消费的时候都会执行给定的消费函数; Stream.of(“1”,”2”,”3”).peek(e-> System.out.Println(e)).collect(Collectors.toList());

limit

对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

skip

返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

forEach

遍历

forEachOrdered

与forEach一样,只不过每次遍历结果都是相同的,forEach不是固定的序列

generate

给定一个生成函数,生成一个无限的队列 Stream.generate(Math::random);

iterate

也是生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环

Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);

collect

汇聚函数

方法参数 返回类型 作用 示例
toList List<T > 把流中的参数汇聚成List集合 示例:List<Menu> menus=Menu.getMenus.stream().collect(Collectors.toList())
toSet Set<T> 把流中所有元素收集到Set中,删除重复项 Set<Menu> menus=Menu.getMenus.stream().collect(Collectors.toSet())
toCollection Collection<T> 把流中所有元素收集到给定的供应源创建的集合中 ArrayList<Menu> menus=Menu.getMenus.stream().collect(Collectors.toCollection(ArrayList::new))
Counting Long 计算流中元素个数 Long count=Menu.getMenus.stream().collect(Collectors.counting());
SummingInt Integer 对流中元素的一个整数属性求和 Integer count=Menu.getMenus.stream().collect(Collectors.summingInt(Menu::getCalories))
averagingInt Double 计算流中元素integer属性的平均值 Double averaging=Menu.getMenus.stream().collect((Collectors.averagingInt(Menu::getCalories))
Joining String 连接流中每个元素的toString方法生成的字符串 String name=Menu.getMenus.stream().map(Menu::getName).collect(Collectors.joining(“, “))
maxBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的optional 如果为空返回的是Optional.empty() Optional<Menu> fattest=Menu.getMenus.stream().collectCollectors.maxBy(Menu::getCalories))
minBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的optional如果为空返回的是Optional.empty() Optional<Menu> lessest=Menu.getMenus.stream().collect(minBy(Menu::getCalories))
Reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用binaryOperator与流中的元素逐个结合,从而将流归约为单个值 int count=Menu.getMenus.stream().collect(reducing(0,Menu::getCalories,Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个转换器,对其结果应用转换函数 Int count=Menu.getMenus.stream().collect(collectingAndThen(toList(),List::size))
groupingBy Map<K,List<T>> 根据流中元素的某个值对流中的元素进行分组,并将属性值做为结果map的键 Map<Type,List<Menu>> menuType=Menu.getMenus.stream().collect(groupingby(Menu::getType))
partitioningBy Map<Boolean,List<T>> 根据流中每个元素应用谓语的结果来对项目进行分区 Map<Boolean,List<Menu>> menuType=Menu.getMenus.stream().collect(partitioningBy(Menu::isType));

reduce

  • Optional<t> reduce(BinaryOperator<t> accumulator);

List <integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println(“ints sum is:” + ints.stream().reduce((sum, item) -> sum + item).get());

可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。这个方法返回值类型是Optional,这是Java8防止出现NPE的一种可行方法,后面的文章会详细介绍,这里就简单的认为是一个容器,其中可能会包含0个或者1个对象。

  • T reduce(T identity, BinaryOperator<t> accumulator);
    这个定义上上面已经介绍过的基本一致,不同的是:它允许用户提供一个循环计算的初始值,如果Stream为空,就直接返回该值。而且这个方法不会返回Optional,因为其不会出现null值。

count

个数

allMatch

是不是所有都满足给定的条件

anyMatch

是不是存在某一个元素满足给定的条件

findFirst

返回第一个,返回的是Optional值

noneMatch

是否没有一个元素满足给定的条件

max和min

给定的比较条件,返回最大最小值

参考
好文收藏