Eric 发布的文章

Linux 模块学习笔记

  1. 使用 lsmod 命令查看已经load 的内核模块, 它其实读取的是 /proc/modules 的信息
  2. 当系统需要某个内核模块的功能的时候, 如果该模块还么有加载, 作为 daemon 的 kmod 进程就会使用 modprobe 命令去加载该模块;
  3. modprobe 可以接受模块的名称, 如: softdog, 或者该模块的别名, 如: char-major-10-30. 标准名称和别名的映射在文件 /etc/modprobe.conf 中, 如: alias char-major-10-30 softdog;
  4. 模块的依赖关系在 /lib/modules/version/modules.dep 中;
  5. depmod 会创建新的依赖关系;

以上内容可能过时, 来源: https://tldp.org/LDP/lkmpg/2.6/html/x44.html

Linux kprobes & uprobes, static tracepoint knowledge links

  1. Kernel Probes: https://www.kernel.org/doc/Documentation/kprobes.txt#:~:text=Kprobes%20enables%20you%20to%20dynamically,when%20the%20breakpoint%20is%20hit.
  2. Uprobe: https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt
  3. https://kernelnewbies.org/Linux_3.5#Uprobes:_userspace_probes
  4. https://lwn.net/Articles/499190/
  5. static tracepoint and how it works: https://www.kernel.org/doc/Documentation/trace/tracepoints.txt

关于 Connection Stacking 的思考

在 eBay, 对于线上应用的监控指标, 有个非常重要的报警数据就是 Connection Stacking alerts. 它是指某个应用的处理能力不足, 导致进来的连接(Connection)都堆积到应用前面的 Load Balancer 上. 其实监控的是 LB 上面的连接数是不是比平时多了很多, 如果多了很多, 那么就显示在这个报警仪表盘上.

什么情况下会发生 Connection Stacking? 通常有2种情况会发生 Connection Stacking: 1). 来访流量确实突然多了; 2). 进来流量还是跟往常一样, 该应用的处理能力变差了.

对于第一种情况, 也有2种细分: 一种是流量确实增多, 可是业务的平均处理时间几乎没啥变化, 或者还在合理范围内, 那么可以不去管它; 另外一种是流量的增加导致业务的平均处理时间变长, 应用的处理能力变弱, 这会导致各种预想不到的后果, 如用户在页面超时, 增加来自上游的 retry(retry storm), 或者导致该应用最终垮掉(当然这里会有各种自动扩容).

对于第二种情况, 在流量不变的情况下, 应用的处理能力变弱, 可能有各种因素. 不过我们还是大致可以分为三类. 1) 应用本身的问题导致处理变慢, 比如某些线程死锁, 导致正常处理业务的线程减少; 比如内存泄漏, 导致可用的内存减少, Java GC 时间增加; 比如缓存大量没命中; 比如业务根据用户的输入, 需要处理大量的正则表达式计算 等; 2) 依赖的其它下游其它应用服务导致本应用的变慢. 微服务的广泛使用, 一个中间层的应用通常要访问几个甚至十几个下游其它服务, 如果某个服务变慢, 同时设置的 timeout 时间或者 retry 次数不合理, 基本会导致从该服务到用户端的连锁变慢; 3) 依赖的数据库服务变慢. 数据库的种类不断增加, 企业应用使用的数据库的场景也逐渐增多. 数据库的 session 数量不够, 或者在处理大规模批处理, 或者数据库其它操作导致的锁, 都会导致访问数据库变慢.

如果要解决 Connection Stacking 问题, 就要分清当前是那种情况, 辨证施治. 对于上面提到的很多数据, 我们都有监控指标, 很容易的辨别出是那种问题, 比如: 进来请求的数据能分清是不是流量增加, 机器级别对于数据库访问的数据指标能分清是不是数据库变慢, 对于各种下游 service 调用的监控指标能反映是不是下游应用变慢;

今天要考虑的问题是针对上面第二种情况的第二种子情况: 即由下游应用变慢导致的本应用变慢. 如果我们不看各种基于微服务客户端的指标, 比如 Hystrix 暴露的指标, 有没有基于操作系统的数据能反映这种情况?

既然是下游调用, 基本是网络调用, 那么就是走网络连接, 绝大多数都是 TCP 不是 UDP, 另外一般的对于下游的服务, 我们都是使用443 或者 80 端口. 那么根据这些约束条件, 能找出某些基于 TCP 连接的数据, 能帮我诊断类似的问题吗?

在各种 Linux 的网络命令当中 ss 是一个比较好的反映网络连接的命令, 让我们看看下面这个 ss 命令的输出:

myhost:~$ ss -it '( dport = :443 or dport = :80 )'
State      Recv-Q Send-Q    Local Address:Port   Peer Address:Port                                                             
ESTAB      0      0         10.33.133.212:47314    10.33.119.204:http                                                       
     bbr wscale:8,2 rto:204 rtt:3.944/4.558 ato:40 mss:1460 cwnd:142 bytes_acked:1605128 bytes_received:1616022 segs_out:2857 segs_in:2977 send 420.5Mbps lastsnd:74840 lastrcv:74832 lastack:74832 pacing_rate 1946.1Mbps retrans:0/8 rcv_rtt:1903.35 rcv_space:96431
ESTAB      0      0         10.33.133.212:59648    10.166.224.89:https                                                          
     bbr wscale:8,7 rto:204 rtt:0.151/0.022 ato:40 mss:1460 cwnd:143 bytes_acked:832208 bytes_received:253799 segs_out:784 segs_in:610 send 11061.2Mbps lastsnd:32 lastrcv:28 lastack:28 pacing_rate 1437.4Mbps rcv_rtt:1924.95 rcv_space:37969

使用 ss 的 -i 选项, 可是使我们看到更多关于连接的详细信息, 这里我们关心的有:
lastsnd, lastrcv, lastack,
-- 未完待续

Linux /dev/ 目录下的一些特殊文件目录

总结一些 /dev 目录下的特殊文件或目录

  1. /dev/null
    空文件, 可以往里面丢任何不想要的东西, 从里面读只能对到一个空
  2. /dev/zero
    空文件, 可以往里面丢任何不想要的东西, 从里面读能返回一个一直有空值的流, 一直返回空值
  3. /dev/shm
    shm = shared memory, 是个目录, 在内存里面开辟的tmpfs文件系统, 所以操作极快. 往里面填文件, 很快占满内存.
  4. /dev/random /dev/urandom /dev/arandom
    通过搜集系统驱动和其它源的信息, 产生伪随机数. 上述三个有区别, 比如在是不是 block 操作;

JDK 8 new features

  1. Lambda Expressions

    1. A lambda expression is an anonymous function. A function that doesn’t have a name and doesn’t belong to any class;
    2. Syntax: (parameter_list) -> {function_body} 如果只有1个参数,可以省去括号, 如果函数体只有一句,可以省略大括号;
    3. 一个方法method(函数function)包含4部分: 方法名, 参数列表, 方法体, 返回值类型. 一个 lambda 表达式: 1)没有名字; 2) 有参数列表; 3) 有函数体; 4) 没有返回值类型, 但是函数可以返回值, JVM 动态侦测类型;
    4. Java 8 之前, 很多地方我们使用匿名内部类, 8及之后, 都可以使用 lambda 表达式来替换;
    5. 通常使用 lambda 表达式的地方 -> 函数式接口(只有一个抽象的方法), 比如 Callable, Runnable, 各种 Listener;
  2. Method References 方法引用
    它通常是 lambda 表达式的一种缩写形式 str -> System.out.println(str) -> System.out::println
    4种类型的方法引用:

    1. Method reference to an instance method of an object – object::instanceMethod
    2. Method reference to a static method of a class – Class::staticMethod
    3. Method reference to an instance method of an arbitrary object of a particular type – Class::instanceMethod
    4. Method reference to a constructor – Class::new
  3. Functional Interfaces 函数式接口

    1. An interface with only single abstract method is called functional interface(Single Abstract Method interfaces);
    2. JDK 预先定义的函数式接口 链接;
    3. 可选的 Annotation: @FunctionalInterface 编译时能侦测是不是符合函数式接口定义;
    4. 定义的时候, 除了这个唯一的抽象的方法, 可以有任何多个非抽象的实例方法或静态方法;
    5. Java 8 之前通常使用匿名内部类来实现这种接口, Java 8 及以后可以使用 lambda 表达式;
  4. 接口增加 default method and static method

    1. Java 8 之前接口只能有抽象方法, 所有的方法默认都是 public & abstract;
    2. Java 8 使接口可以有 default 方法和 static 方法;
    3. default method and static method 可以是已存在的接口添加新的功能而不影响原有逻辑;
    4. static method 类似 default method, 只是实现者不能 override 这些方法;
    5. default method 值方法签名前加 default 关键字;
    default void myDefaultMethod(){ ... }
    1. 抽象类可以有构造函数, interface 不行. 接口侧重定义规范, 抽象类侧重实现整体, 细节留空;
    2. 一个类实现多个接口中如果有相同的 default 方法, 编译会报错, 需要在实现类中解决冲突;
  5. Stream API (java.util.stream)

    1. using streams we can perform various aggregate operations on the data returned from collections, arrays, Input/Output operations;
    2. Stream 可顺序也可以通过并行执行(Parallel execution)的方式(理论上,实际不一定)加快执行速度;

      1. stream.sequential()
      2. stream.parallel()
    3. 步骤: 1) 一次创建 Stream; 2) 0或多个中间操作; 3) 一次终止操作;
    4. 例子: Arrays.asList("line0", "line1").stream().filter(str->str.length()<6).count()
    5. 常见的操作:
      Intermediate operations:

      1. filter - Exclude all elements that don't match a Predicate.
      2. map - Perform a one-to-one transformation of elements using a Function.
      3. flatMap - Transform each element into zero or more elements by way of another Stream.
      4. peek - Perform some action on each element as it is encountered. Primarily useful for debugging.
      5. distinct - Exclude all duplicate elements according to their .equals behavior. This is a stateful operation.
      6. sorted - Ensure that stream elements in subsequent operations are encountered according to the order imposed by a Comparator. This is a stateful operation.
      7. limit - Ensure that subsequent operations only see up to a maximum number of elements. This is a stateful, short-circuiting operation.
      8. skip - Ensure that subsequent operations do not see the first n elements. This is a stateful operation.

      Terminal operations:

      1. forEach - Perform some action for each element in the stream.
      2. toArray - Dump the elements in the stream to an array.
      3. reduce - Combine the stream elements into one using a BinaryOperator.
      4. collect - Dump the elements in the stream into some container, such as a Collection or Map.
      5. min - Find the minimum element of the stream according to a Comparator.
      6. max - Find the maximum element of the stream according to a Comparator.
      7. count - Find the number of elements in the stream.
      8. anyMatch - Find out whether at least one of the elements in the stream matches a Predicate. This is a short-circuiting operation.
      9. allMatch - Find out whether every element in the stream matches a Predicate. This is a short-circuiting operation.
      10. noneMatch - Find out whether zero elements in the stream match a Predicate. This is a short-circuiting operation.
      11. findFirst - Find the first element in the stream. This is a short-circuiting operation.
      12. findAny - Find any element in the stream, which may be cheaper than findFirst for some streams. This is a short-circuiting operation.
    6. stream 可以 infinite;
    7. stream 处理可以短路(Short-circuiting), 不在执行剩下的, 对于无限 stream 最有用;
  6. Optional

    1. 改变编程习惯, 避免 NullPointerException - 原来直接用, 现在用各种方法;
    2. A container object which may or may not contain a non-null value;
    3. 创建: 1) Optional.empty(), 2) Optional.of(T value), 3) Optional.ofNullable(T value);
    4. 判断: isPresent();
    5. 获得: get(), 若null -> NoSuchElementException;
    6. 获得带 fallback 机制:

      1. 若空返回 other: orElse(T other)
      2. 若空执行函数接口返回值: orElseGet(Supplier<? extends T> other)
      3. 若空抛出特定异常: orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
    7. 其它

      1. 非空并且满足predicate filter(Predicate<? super T> predicate);
      2. map(Function<? super T, ? extends U> mapper)
      3. flatMap(Function<? super T, Optional<U>> mapper)
  7. StringJoiner 类似 guava 的 Joiner, 前缀, 后缀, 分隔符.
  8. Arrays.parallelSort -> 多线程排序加速
  9. java.util.function 一些常见的函数

    1. Function<T, R> - take a T as input, return an R as ouput
    2. Predicate<T> - take a T as input, return a boolean as output
    3. Consumer<T> - take a T as input, perform some action and don't return anything
    4. Supplier<T> - with nothing as input, return a T
    5. BinaryOperator<T> - take two T's as input, return one T as output, useful for "reduce" operations
  10. java.time 包

refer:

  1. https://beginnersbook.com/2017/10/java-8-features-with-examples/
  2. https://www.techempower.com/blog/2013/03/26/everything-about-java-8/
  3. https://www.javatpoint.com/java-8-features
  4. https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html