0%

做了一段时间的即时通讯IM应用,有些心得,特来分享分享。

IM

IM,又称为即时通讯。

既然是即时,那就需要和客户端建立一个长连接。
对于长连接需要有连接保活心跳机制而长连服务器的IP地址又需要进行下发等。

总体划分如下:

  • 服务器地址下发
  • 网关层
  • API层
  • 服务层
  • 队列机层

用户客户端打开应用之后,将会向服务器发起一个HTTPS请求,服务端根据长连服务器负载情况将要下发的地址进行排序,客户端根据获取到的长连服务器地址顺序尝试连接,成功建立连接之后开始进行通信。

每一条上行及下行的消息都需要与网关层进行交互,在连接期间,需要与网关层进行心跳检测链路问题,消息经过encode处理之后发送到网关层,网关层直接调用API层的RPC透传到API层去处理。

API层收到来自网关层的RPC请求之后,先进行decode,然后解析出相应的header、body,根据header中定义的tag和type去调用相应的方法。这些方法除了业务逻辑外,另外就是通过RPC或者HTTP去调用各种所需的服务,如:用户服务、私信服务、群聊服务。

服务层收到API层的请求之后,生成消息版本号,将消息入库,并将消息异步写入队列之中,返回处理结果给API层。

队列机这一层负责接收、处理及转发多种服务写入的消息,然后将处理后的消息pub到Redis中,包括离线缓存队列,由长连服务器去sub,然后下推给客户端。

需要解决的问题
  • 消息乱序问题
  • 消息可靠性问题

给定一个无序数组,使用归并排序法对数组进行从小到大排序。

原理: 将数组一分为二,二分为四,直到不可切分,然后排序、合并。(先分后治)

归并排序

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
@Test
public void testMergeSort() {
int[] arr = {4, 2, 1, 5, 7, 6, 3};
mergeSort(arr, 0, arr.length - 1);
// -- output : [1, 2, 3, 4, 5, 6, 7]
System.out.println(Arrays.toString(arr));
}

private void mergeSort(int[] arr, int low, int high) {
if (low < high) {
//中心点
int mid = low + (high - low) / 2;
//左区间排序
mergeSort(arr, low, mid);
//右区间排序
mergeSort(arr, mid + 1, high);
//合并左右区间排序结果
merge(arr, low, mid, high);
}
}

private void merge(int[] arr, int low, int mid, int high) {
//左区间起点,终点为mid
int i = low;
//右区间起点,终点为high
int j = mid + 1;
//临时数组,用来存储排序结果,大小为: high - low + 1
int[] temp = new int[high - low + 1];
int t = 0;

//归并
while (i <= mid || j <= high) {
//当左右区间都未排完序
if (i <= mid && j <= high) {
if (arr[i] <= arr[j]) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
//当左区间还有未排序序的
else if (i <= mid) {
temp[t++] = arr[i++];
}
//当右区间还有未排序序的
else {
temp[t++] = arr[j++];
}
}

//将排序结果赋值给原数组
for (int e : temp) {
arr[low++] = e;
}
}

给定一个无序数组,使用快速排序法对数组进行从小到大排序。

原理: 定义一个基准值,同时遍历数组高、低位,并与之比较。

当高位比基准值大的时候,高位索引 j 往低位移动,也就是j--,当遇到一个比基准值小的数,就把该值赋给低位。

当低位比基准值小的时候,低位索引 i 往高位移动,也就是i++,当遇到一个比基准值大的数,就把该值赋给高位。

重复以上,直到i < j,然后,将基准值赋给低位,递归去排序基准值左侧部分和基准值右侧部分,每次递归都需要找一个新的基准值。

快速排序

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
 @Test
public void testQuickSort() {
int[] arr = {1, 3, 2, 6, 5, 4, 8, 9, 7, 0, 10};
quickSort(arr, 0, arr.length - 1);
// -- output : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
System.out.println(Arrays.toString(arr));
}

private void quickSort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
//低位索引
int i = low;
//高位索引
int j = high;
//找一个基准值
int temp = arr[low];
while (i < j) {
//将高位和基准值比较
while (i < j && arr[j] >= temp) {
j--;
}
//将高位不符合条件的值赋给低位
if (i < j) {
arr[i] = arr[j];
}
//将低位和基准值比较
while (i < j && arr[i] <= temp) {
i++;
}
//将低位不符合条件的值赋给低位
if (i < j) {
arr[j] = arr[i];
}
}
//将基准值赋给i
arr[i] = temp;
//排序low到i-1部分的
quickSort(arr, low, i - 1);
//排序i+1到high部分的
quickSort(arr, i + 1, high);
}

给定一个有序数组,查找数组中某个元素的索引。

原理: 每次将搜索区间一分为二,比较目标值和中心值大小,再决定下一步要从左区间搜索还是右区间搜索。

二分查找

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
@Test
   public void testBinarySearch() {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// -- output : 1
System.out.println(binarySearch(arr, 2));
}

private int binarySearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
//每次找到中心点,且要防止整数溢出。
int mid = low + (high - low) / 2;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] < key) {
//如果目标值大于中心值,则从右侧开始找
low = mid + 1;
} else {
//如果目标值小于中心值,则从左侧开始找
high = mid - 1;
}
}
return -1;
}

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment