Enda Lin

keep foolish, keep sharp


  • Home

  • Categories

  • About

  • Archives

  • Tags

操作系统面试题之进程与线程相关

Posted on 2019-11-14 |
Words count in article 472 | Reading time 1

进程(线程 + 内存 + 文件/网络句柄)

  • 内存:逻辑内存
  • 文件/网络句柄:为所有线程所共有
  • 线程:进程所创建的,进程是线程的容器

孤儿进程

指的是父进程死掉的子进程,这些孤儿进程会被init进程(进程号为1)所收养,由init 进程对它们完成状态收集工作

僵尸进程

一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。

线程(栈 + PC + TLS)

  • 栈
  • PC(程序计数器):PC就是指向当前的指令,而这个指令是放在内存中。 每个线程都有一串自己的指针,去指向自己当前所在内存的指针
  • TLS(Thead Local Storage): 存储线程所独有的数据

进程与线程的区别

  • 进程是资源调度的最小单位,线程是系统调用的最小单位
  • 进程有自己独立的内存空间,线程可以共享进程的内存空间

死锁

原因

  • 系统资源不足
  • 进程推进的顺序不合适
  • 资源分配不当

导致死锁的四个必要条件

  • 一个进程一次只能访问一个资源,其它进程不能访问已经分配的资源
  • 一个进程在等待其它资源的时候,继续占有已经抢占到的资源
  • 一个进程不能强行抢占另一个进程的资源
  • 存在一个封闭的进程链,导致每一个进程都在等待下一个进程的资源

银行家算法

操作系统面试题之存储器相关

Posted on 2019-11-06 |
Words count in article 2k | Reading time 6

存储器的层次结构

寄存器、高速缓存、主存储器、磁盘缓存、固定磁盘、可移动的存储介质

逻辑地址和物理地址

用户能看到的程序代码、变量、堆找的地址称之为逻辑地址,它们所在的实际内存区域实际地址是物理地址

将逻辑地址和物理地址分开的好处

  • 实现内存隔离
  • 实现进程和内核保护

内核空间与用户空间

内核空间: 程序地址空间中安排给操作系统使用的部分,又称之为虚拟存储器

用户空间: 程序地址空间中安排给用户程序使用的部分,又称为用户虚拟存储器

分页存储管理和页表

  • 把装入模块(即链接后的可执行程序)切分为一系列等长的片段,每个片段称为一个页面(页面长度为512B、1KB、2KB、4KB等,小于动态分区的很多碎片),页号依次为0、1、2、…,(具有n-1个页面的进程大小是多少字节)
  • 也把系统物理内存划分为一系列等长的片段,每个片段称为一个物理块(或一个页帧),物理块长度与页面长度相同,块号为0、1、2、…
  • 用户程序运行时,操作系统将其各个页面装载到一系列任意的物理块(地址可能不连续)
Read more »

Java面试题之树相关

Posted on 2019-11-06 |
Words count in article 797 | Reading time 2

B 树

binary search tree,称为二叉搜索树、二叉排序树或者二叉查找树,简称BST

B 树,即二叉搜索树

  • 所有非叶子结点都有左右子节点
  • 所有结点都会存储一个关键字
  • 非叶子节点的左子节点小于其关键字,右子节点大于其关键字

AVL 树

AVL 树是自平衡二叉查找树,在AVL 树中,任何节点的两个子树的高度最大差别为1,查找、插入和删除的平均和最坏情况都是O(log n)

B+ 树

特点

  • 所有关键字都出现在叶子节点中,叶子节点和叶子节点之间是以链表的方式来存储的
  • 叶子节点是有序的

红黑树

红黑树是满足以下性质的二叉搜索树

  • 每个结点是红色的或者黑色的
  • 根结点是黑色的
  • 如果结点是红色的,其左右叶子结点都是黑色的

    保证了从根节点到叶子节点的最长路径的长度不会超过任何其他路径的两倍

  • 每个叶子结点都是黑色的
  • 对于每个结点,从该结点到其所有子孙叶结点的路径中所包含的黑色结点数量必须相同。

红黑树和B 树的应用场景

  • 红黑树多用在内部排序,即全放在内存中的,STL的map和set的内部实现就是红黑树。

  • B+树多用于外存上时,B+也被成为一个磁盘友好的数据结构。

红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能

为什么用二叉树 而不用散列表

  • 第一,散列表中的数据是无序存储的,如果要输出有序的数据,需要先进行排序。而对于二叉查找树来说,我们只需要中序遍历,就可以在O(n)的时间复杂度
    内,输出有序的数据序列。
  • 第二,散列表扩容耗时很多,而且当遇到散列冲突时,性能不稳定,尽管二叉查找树的性能不稳定,但是在工程中,我们最常用的平衡二叉查找树的性能非常稳
    定,时间复杂度稳定在O(logn)。
  • 第三,笼统地来说,尽管散列表的查找等操作的时间复杂度是常量级的,但因为哈希冲突的存在,这个常量不一定比logn小,所以实际的查找速度可能不一定
    比O(logn)快。加上哈希函数的耗时,也不一定就比平衡二叉查找树的效率高。
  • 第四,散列表的构造比二叉查找树要复杂,需要考虑的东西很多。比如散列函数的设计、冲突解决办法、扩容、缩容等。平衡二叉查找树只需要考虑平衡性这一
    个问题,而且这个问题的解决方案比较成熟、固定。

参考

  • B树、B+树、AVL树、红黑树
  • 对B+树,B树,红黑树的理解

Java面试题之JVM相关

Posted on 2019-10-29 |
Words count in article 2.4k | Reading time 9

JVM 堆

在Java 中,堆被划分为两个不同的区域:新生代和老年代

新时代:被划分为三个区域:Eden、From survivor 和 To survivor

划分的目的是为了使JVM 更好地管理JVM 内存对象,包括内存的分配以及内存的回收

参考

Java GC、新生代、老年代

GC

GC Roots

GC Roots 是tracing GC 的根集合,是一组必须活跃的应用

通过一系列名为“GC roots” 的对象为起始点,从这个被成为“GC roots” 的对象向下搜索,如果一个对象到GC roots 没有任何的引用链相连,则说明该对象不可用。也就是说,以给定的一个集合的引用作为根出发点,通过引用关系遍历对象图,能被遍历到的(可到达的对象)则被判定为存活,不可达的则被判定为死亡。

GC roots set

  • 虚拟机栈(栈帧中的局部变量表)中应用的对象
  • 方法区中的类静态属性应用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI (Native 方法) 应用的对象

四大垃圾回收算法

  • 引用计数
  • 复制拷贝
  • 标记-清除算法(Mark-Sweep) (优:节约空间,缺:产生内存碎片)
  • 标记 - 压缩

主要的垃圾回收器

下图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器。Hotspot实现了如此多的收集器,正是因为目前并无完美的收集器出现,只是选择对具体应用最适合的收集器。

  • 串行垃圾回收器(-XX:+UseSerialGC)

    串行垃圾回收器通过持有应用程序所有的线程进行工作。它为单线程环境设计,只使用一个单独的线程进行垃圾回收,通过冻结所有应用程序线程进行工作,所以可能不适合服务器环境。它最适合的是简单的命令行程序(单CPU、新生代空间较小及对暂停时间要求不是非常高的应用)。是client级别默认的GC方式。

    开启之后会使用,新时代和老年代都会使用串行回收收集器,新生代使用标记-复制算法,老年代使用标记-整理算法

  • ParNew 收集器(-XX:+UseParNewGC)

    只对新生代起作用
    ParNew 回收器也要冻结用户的工作线程,在新时代中使用多线程来运行标记-复制算法来进行垃圾回收,在老年期还是使用单线程和标记-整理算法来进行垃圾回收
    注意: ParNew 和SerialOld 已经不再被推荐使用
    备注:-XX:ParallelGCThreads 限制线程数量,默认起与CPU 相同数目的线程

  • Parallel Scavenge 收集器(-XX:+UseParallelGC)

    它是JVM的默认垃圾回收器。与串行垃圾回收器不同,它使用多线程进行垃圾回收。相似的是,当执行垃圾回收的时候它也会冻结所有的应用程序线程。
    新生代和老年代都用多个线程并行去运行

  • CMS并发标记扫描垃圾回收器(-XX:+UseConcMarkSweepGC)

    用户线程和垃圾回收线程同时执行,适用于对响应时间有要求的公司
    只要在老年代开启了CMS,新生代会自动开启ParNew,Serial Old会作为CMS 出错的后备收集器

    四个步骤:

    • 初始化标记Initial Mark(冻结用户线程):只是标记一下GC Roots能直接关联的对象,速度很快
    • 并发标记Concurrent Mark(GC 线程和用户线程一起工作):进行GC Roots跟踪过程,这是主要的标记过程,标记所有的对象
    • 重新标记Remark(冻结用户线程):为了修正正在并发标记期间,因用户程序继续运行而导致标记发生变动的那一部分对象的标记记录,任然需要暂停所有工作线程。(二次确认)
    • 并发清除Concurrent sweep(GC 线程和用户线程一起工作)
  • G1垃圾回收器(-XX:+UseG1GC)

    G1垃圾回收器适用于堆内存很大的情况,他将堆内存分割成不同的区域,并且并发的对其进行垃圾回收,不会产生许多内存碎片,G1 在停顿时间添加了预测机制,用户可以指定停顿时间
    G1 特点

    • G1 能充分利用CPU,多核环境的优势,尽量缩短STW
    • G1 整体采用标记-整理算法,局部通过复制算法,不会产生内存碎片
    • 宏观上看G1 不再区分年轻代和老年代,把内存划分为多个Region 区
    • G1 收集器在小范围内进行年轻代和老年代的区分,不再是物理隔离,是一部分Region 的集合而且不需要连续

如何选择合适的垃圾收集器

  • 单CPU 或者小内存,单机程序

    -XX:+UseSerialGC

  • 多CPU,需要最大的吞吐量,如后台计算型应用

    -XX:+UseParallelGC or -XX:+UseParallelOldGC

  • 多CPU,追求低停顿时间,需要快速响应

    -XX:+UseConcMarkSweepGC -XX:+ParNewGC

查看默认的垃圾回收器

1
2
3
4
5
6
7
F:\IDEA\Test>java -XX:+PrintCommandLineFlags -version

-XX:G1ConcRefinementThreads=8 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=267006528 -XX:MaxHeapSize=4272104448 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+
SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+12-b304.10)
OpenJDK 64-Bit Server VM (build 11.0.3+12-b304.10, mixed mode, sharing)

结果:-XX:+UseG1GC

概念补充

  • 可控制的吞吐量 = 运行用户代码的时间/(运行用户代码的时间+垃圾收集器),高吞吐量意味着利用CPU 的效率高

参考

浅析JAVA的垃圾回收机制(GC)

深入理解JVM(3)——7种垃圾收集器

JVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)

Read more »

Java面试题之线程池

Posted on 2019-10-29 |
Words count in article 1.6k | Reading time 7

Runnable and Callable

  • Runnable 没有返回值
  • Callable 有返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyThread implements Runnable {
@Override
public void run () {

}
}

class MyThread2 implements Callable<Integer> {
@Override
public Integer call() {
return 123;
}
}
public class Test {
public static void main(String[] args) throws Exception {
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread2());

Thread thread = new Thread(futureTask, "aa");
thread.start();

// 阻塞等待线程执行结果
System.out.println(futureTask.get());
}
}
Read more »

Java面试题之阻塞队列

Posted on 2019-10-28 |
Words count in article 377 | Reading time 1

阻塞队列(BlockingQueue)

  • 当阻塞队列为空的时候,从队列中获取元素的操作会被阻塞
  • 当阻塞队列为满的时候,向队列中添加元素的操作会被阻塞

种类

  • ArrayBlockingQueue: 由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue: 由链表结构组成的有界阻塞队列(大小值默认为Integer.MAX_VALUE)
  • PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • SynchronousQueue: 不存储元素的阻塞队列,也即有且只有一个元素的队列(一个put 必须要对应一个take)
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由列表结构组成的双向阻塞队列
Read more »

Java 面试题之锁相关

Posted on 2019-10-27 |
Words count in article 1.2k | Reading time 5

公平锁/非公平锁

并发包中的ReentrantLock 可以创建公平锁或者非公平锁,默认是非公平锁

Java 多线程值ReentrantLock 与Condition

公平锁

指多个线程按照申请锁的顺序来获取锁

在并发环境中,每个线程都会先查看锁的FIFO 等待队列,若为空,就占有锁,否则,则进队

非公平锁

指多个线程获取锁的顺序不是按照申请锁的顺序来的,在高并发情况下,有可能会造成优先级反转或者饥饿现象

线程一上来就尝试占有锁,若尝试失败,则再采用类似公平锁的那种策略

优点:非公平锁的吞吐量比公平锁大

对于Synchronized 而言,也是一种非公平锁

可重入锁(递归锁)

指的是同一线程在外层函数获得锁之后,内层的递归函数依然可以获得该锁

换句话说:线程可以进入任何一个它已经拥有的锁同步着的代码块

ReentrantLock/Synchronized 是一个典型的可重入锁

可重入锁作用:避免死锁

自旋锁

指的是在尝试获取锁的时候不会立即阻塞,而是采用循环的方式去尝试获取锁

优点:减少上下文的切换

缺点:循环会消耗CPU

典型例子:CAS(AtomicInteger)

读写锁

  • 读-读共存
  • 读-写不共存
  • 写-写不共存
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

class MyCache {
private volatile Map<String, Object> map = new HashMap<>(16);
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public Object get(String key) {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "号线程读数据");
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "号线程读数据完毕, " + result);
lock.readLock().unlock();
return result;
}

public void put(String key, Object object) {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "号线程写数据");
map.put(key, object);
System.out.println(Thread.currentThread().getName() + "号线程写数据完毕");
lock.writeLock().unlock();
}
}

public class Test {
public static void main(String[] args) {
MyCache myCache = new MyCache();

for (int i = 0;i < 5;i++) {
final int finalI = i;
new Thread(() -> {
myCache.put(String.valueOf(finalI), String.valueOf(finalI));
}, String.valueOf(i)).start();
}

for (int i = 0;i < 5;i++) {
final int finalI = i;
new Thread(() -> { myCache.get(finalI + "");
}, String.valueOf(i)).start();
}
}
}

输出:
1号线程写数据
1号线程写数据完毕
4号线程写数据
4号线程写数据完毕
0号线程写数据
0号线程写数据完毕
2号线程写数据
2号线程写数据完毕
3号线程写数据
3号线程写数据完毕
0号线程读数据
0号线程读数据完毕, 0
2号线程读数据
1号线程读数据
1号线程读数据完毕, 1
3号线程读数据
3号线程读数据完毕, 3
4号线程读数据
4号线程读数据完毕, 4
2号线程读数据完毕, 2

CountDownLatch

让一些线程阻塞,直到其余线程完成为止

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
public class Test {
public static void main(String[] args) throws Exception{
CountDownLatch countDownLatch = new CountDownLatch(6);

for (int i = 0;i < 6;i++) {
int finalI = i;
new Thread(() -> {
System.out.println(finalI + "号同学离开");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}

// countDownWatch 达到0 时才能解锁
countDownLatch.await();
System.out.println("班长离开");
}
}

输出:
0号同学离开
4号同学离开
1号同学离开
3号同学离开
2号同学离开
5号同学离开
班长离开

CyclicBarrier

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
public class Test {
public static void main(String[] args) throws Exception {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("出发"));

for (int i = 0;i < 7;i++) {
final int index = i;
new Thread(() -> {
System.out.println(index + "号客人到达");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}

输出:
0号客人到达
3号客人到达
2号客人到达
1号客人到达
5号客人到达
4号客人到达
6号客人到达
出发

Semaphore

信号量,控制对共享资源的访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public static void main(String[] args) throws Exception {
Semaphore semaphore = new Semaphore(3);

for (int i = 0; i < 6; i++) {
final int index = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "进站");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "离开");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}

AQS

如果被请求的共享资源处于空闲状态,则当前请求资源的线程就会设置为工作线程,并且将当前资源设置为锁定状态;如果被请求的共享资源处于锁定状态,则需要一套线程阻塞以及线程被唤醒时锁分配的机制,这个机制是使用CLH 队列锁实现的,将暂时获取不到锁的线程放到队列中

典型例子:ReentrantLock, Semaphore, ReentrantReadWriteLock等等

AQS 定义两种资源的共享方式

  • Exclusive(独占):只能有一个线程去独占共享资源,公平锁、非公平锁(Synchronized)
  • Share(共享):多个线程可以同时去执行,如Samephore、CycliBarrier、CountDownWatch

flume

Posted on 2019-10-11 |
Words count in article 65 | Reading time 1

Flume 定义

Flume 是一个高可用、高可靠、分布式的海量日志采集、聚合和传输的系统,它基于流式框架

Flume 作用在于实时读取服务器本地磁盘的文件夹,将数据写入HDFS 中

kafka

Posted on 2019-10-08 |
Words count in article 936 | Reading time 3

kafka

kafka 是一个分布式消息队列,kafka 对消息保存是根据Topic 进行归类,发送者称为Producer,消息接受者称为Consumer,kafka 集群有多个kafka 实例组成,每个实例称为 broker

无论是kafka 集群还是consumer 都依赖Zookeeper 集群保存一些meta 信息, 来保证系统的可用性

在kafka 集群中,有leader 节点和follower 节点,follower 节点是备份数据用的,客户端只有访问leader 节点时才能得到响应,这个与Zookeeper 不同

在Zookeeper 集群中,客户端可以访问leader 节点也可以访问follower 节点,如果客户端访问follower 节点,如果是写请求,follower节点会把写请求转发到leader 节点中,leader 节点在执行完读写请求后,会把执行结果返回给follower 节点,follower 节点再把结果返回给客户端;如果是读请求,follower 会直接读

同一组的消费者不能同时消费同一个分区的kafka

部署

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: '3'

services:
kafka1:
image: wurstmeister/kafka
restart: always
hostname: kafka1
container_name: kafka1
ports:
- 9092:9092
environment:
KAFKA_ADVERTISED_HOST_NAME: kafka1
KAFKA_ADVERTISED_PORT: 9092
KAFKA_ZOOKEEPER_CONNECT: zoo1:2181,zoo2:2181,zoo3:2181
KAFKA_LISTENERS: PLAINTEXT://kafka1:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka1:9092
volumes:
- ./kafka1:/kafka
extra_hosts:
- "zoo1:10.60.2.128"
- "zoo2:10.60.2.128"
- "zoo3:10.60.2.128"

kafka2:
image: wurstmeister/kafka
restart: always
hostname: kafka2
container_name: kafka2
ports:
- 9093:9092
environment:
KAFKA_ADVERTISED_HOST_NAME: kafka2
KAFKA_ADVERTISED_PORT: 9092
KAFKA_ZOOKEEPER_CONNECT: zoo1:2181,zoo2:2181,zoo3:2181
KAFKA_LISTENERS: PLAINTEXT://kafka2:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka2:9092
volumes:
- ./kafka2:/kafka
extra_hosts:
- "zoo1:10.60.2.128"
- "zoo2:10.60.2.128"
- "zoo3:10.60.2.128"

kafka3:
image: wurstmeister/kafka
restart: always
hostname: kafka3
container_name: kafka3
ports:
- 9094:9092
environment:
KAFKA_ADVERTISED_HOST_NAME: kafka3
KAFKA_ADVERTISED_PORT: 9092
KAFKA_ZOOKEEPER_CONNECT: zoo1:2181,zoo2:2181,zoo3:2181
KAFKA_LISTENERS: PLAINTEXT://kafka3:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka3:9092
volumes:
- ./kafka3:/kafka
extra_hosts:
- "zoo1:10.60.2.128"
- "zoo2:10.60.2.128"
- "zoo3:10.60.2.128"

kafka 生成过程分析

写入方式

producer 采用push 模式将消息发布到broker 中,每条消息都被追加append 到分区patition 中

分区(partition)

消息发送时都会被发送到一个topic,其本质就是一个目录,而topic 是由一些partition logs(分区日志)组成

每一个分区内的消息都是有序的,生产的消息被不断追加到Partition 中,其中的每一个消息被赋予了唯一的offset 值

分区的原因:

  • 方便在集群扩展,每个Partition 可以通过调整以适应它所在的机器,而一个Topic 又可以有多个partition 组成,因此整个集群就可以适应任意大小的数据了
  • 提高并发,以partition 为单位读写

分区原则:

  • 指定了partition, 则直接使用
  • 未指定partition 但指定key,通过key 的value hash出一个partition
  • partition 和key 都未指定,使用轮训选出一个partition

副本Replication

同一个partition 可能会有多个replication,一旦broker 挂了,其上所有partition 的数据都不可被消费,同时也不能再往这些partition 中写入数据,此时,就会从partition 中的replication 中重新选举出一个leader ,producer 和consumer 只与这个leader 交互,其它fllower 从leader 中复制数据

Producer 写入流程

  • producer 先从broker-list 获取partition 的leader
  • producer 将消息发送给该leader
  • leader 将消息写入本地log
  • followers 从leader pull 消息,followers 写完之后想leader 发送ack

leader 写入成功之后,可以立即反馈给客户端,写操作结束,也可以等到followers 写完之后再反馈。而前者的效率是后者的十倍。

Cuscomer

kafka 提供了两套consumer API:高级 API和低级API

高级API
优点:

  • 简单
  • 不需要自行去管理offset,系统通过zookeeper 自行管理
  • 不需要管理分区,副本等情况,系统自动管理

缺点:

  • 不能控制offset,不能细化控制分区、副本等等

低级API

优点:

  • 自主控制offset,管理分区、副本
    缺点:
  • 太复杂

dubbo

Posted on 2019-10-08 |
Words count in article 134 | Reading time 1

分布式基础原理

什么是分布式系统

分布式系统是若干个独立计算机的集合,这个计算机对于用户来说就像单个相关的系统

演变路线:

RPC

远程过程调用,进程间的方法调用

Dubbo

高性能的Java RPC 框架

Dubbo 官网

  • 面向接口的高性能RPC 调用
  • 智能负载均衡
  • 服务自动注册与发现(zookeeper)
  • 高度可扩展能力
  • 运行期流量调度
  • 可视化的服务治理与运维
12…8
Enda Lin

Enda Lin

所有的伟大都是从零开始

79 posts
35 tags
GitHub E-Mail
© 2019 Enda Lin
您是博主的第 位小伙伴 总访问量 次