Fei

非淡泊无以明志,非宁静无以致远

课程参考:

1. 基础篇

  • 导学-应用领域

    • 桌面应用: Windows桌面应用
    • 系统级软件:操作系统,驱动程序,数据库,网络协议
    • 底层架构:Java虚拟机、python解释器、AI核心库
    • 游戏开发:游戏引擎、服务器端、客户端
    • 嵌入式开发:工业控制、智能家电、航空航天、电子通讯
  • 编译与解释:

    • 编译型:由编译器把整个源代码翻译成机器码,最终生成二进制文件,一次性提交给计算机执行。代表:c, c++

    • 解释型:由解释器将代码逐行解释成机器码,并交给计算机执行。代表:python,JavaScript

    • .cpp ==compile==> .obj ==link==>.exe

  • C++标准

    • 98/03: v1.0 (重点)
    • 11: v2.0 (重点)
    • 14
    • 17
    • 20: v3.0

1.1 基础语法

1.1.1 HelloWorld

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
#include<iostream>
using namespace std;
// 主函数
int main() {
// std::cout 标准的输出流对象
// << 输出运算符
// std 命名空间
// :: 作用域解析运算符
// cout 控制台输出
// endl 换行符
std::cout << "Hello, World!" << std::endl;

// 使用using namespace std;后可以省略std::
cout << "Hello, World!" << endl;

string name;
cin >> name; // 从标准输入流读取数据到变量name中

// << name << "!" << endl; 链式输出: 中间的<<连接多个输出项, 不能使用 "+", 因为cout不是字符串类型


cout << "Hello, " << name << "!" << endl;

//system("pause"); // 暂停程序运行,等待用户按键. 直接调用系统命令,不建议使用
cin.get(); // 等待用户按键,更加安全和可移植

welcome(); // 调用自定义函数
return 0;
}

static void welcome() {
cout << "Welcome to C++ programming!" << endl;
}

1.1.2 注释

1
2
3
4
// 单行注释
/** 多行注释
* 和Java相同
*/

1.2 数据类型

1.2.1 变量

  • 变量定义
1
2
int a = 0, b;
b = 10;

注意:变量最好初始化,以避免发生未初始化错误

  • 作用域

在C++中,有“作用域(scope)“的概念,就是指程序中的某一段,某一部分,一般使用{}作为分隔。

定义在所有花括号外的名字具有”全局作用域“, 而在某个花括号内是”块作用域“(局部作用域)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
using namespace std;

int number = 100; // 全局变量

int getNumber() {
return number;
}

int main() {

cout << "number全局变量number的值为: " << number << endl;

int number = 200; // 局部变量,屏蔽全局变量
cout << "number局部变量number的值为: " << number << endl;

cout << ::number << endl; // 访问全局变量,使用作用域解析运算符
cout << getNumber() << endl; // 访问全局变量,通过函数
}

1.2.2 常量

  • 使用#define

    1
    #define ZERO 0 // 宏定义常量, C语言的特性,在C++中不推荐使用
  • 使用const限定符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include<iostream>
    using namespace std;
    #define ZERO 0 // 宏定义常量, C语言的特性,在C++中不推荐使用(因为没有数据类型定义,无法进行类型的安全检查)

    const int MAX_SIZE = 1000; // 全局常量

    int main() {
    const int CON = 100; // 局部常量
    }

1.2.3 基本数据类型

(1) 整数与浮点数

  • short: 16 bit

  • int: 16~32 bit

  • long: 32 bit

  • long long: 64 bit

  • char: 8 bit

    • unsigned char
    • signed char (default)
  • bool: 1 bit

  • float: 32 bit

  • double: 64 bit

  • long double: 64 bit ~ 16 Byte

  • 无符号整型:

    • unsigned int
    • unsigned short
    • unsigned long
    • unsigned long long
  • wchar_t

    • wcout <<

(2) 数值表示

  • 十进制:普通表达 123

  • 八进制:0开头 0123

  • 十六进制:0x开头0x123

  • 默认什么都不加,是int类型

  • l或者L, 表示long类型

  • llLL, 表示long long类型

  • uU, 表示unsigned类型

(3) 字符串

本质是char数组

(4) 赋值时的类型转换

1
2
3
4
5
6
7
bool btrans = 12; // 实际 btrans = 1;
short strans = false; // 实际strans = 0;
float ftrans = true; // 实际ftrans = 1;
int iftrans = 3.22f; // 实际保留整数, iftrans = 3; 浮点数赋值给整数类型,只会保留整数
无符号数溢出会取模
有符号数溢出

1.2.4 运算符

  • 优先级和结合率: 3 - 5 * 2 / (3 % 4)

  • 算术运算符: *, /, %, +, -

  • 关系运算符: <, >, ==, >=, <=, !=

  • 逻辑运算符: &&, ||, &, |, !

  • 条件运算: a == b ? true : false

  • 位运算符

    • 位求反: ~
    • 左移:<<
    • 右移: >>
    • 与:&
    • 或:|
    • 异或:^ 相同为0,相异为1
  • 隐式类型转换

    1
    short s = 15.2 + 3; // 运算时隐式类型转化为double, 赋值时double浮点型隐式转化为整型
  • 强制类型转换

    1
    2
    3
    4
    5
    double d0 = 10 / 3.0; // 隐式类型转换
    double d1 = (double)10 / 3; //c语言风格强转
    double d2 = double(10) / 3; // cpp函数调用风格强转
    double d3 = static_cast<double> (10) / 3; // cpp强制类型转换符,更严格
    cout << "d0 = " << d0 << " d1 = " << d1 << " d2 = " << d2 << " d3 = " << d3 << endl;

1.2.5 复合数据类型

(1) 普通指针

  • 无效指针:

    • int* p没有进行初始化,那么它的内容是不确定的(内存中可能有值,那么解引用时*p可能读到内存中的任意地址,如果读到系统核心区域并进行了修改,可能导致系统异常)
  • 空指针

    • int* p = nullptr:空指针字面量 cpp
    • int* p = NULL 预处理变量(在底层定义的变量(常量)#define NULL 0)
    • int* p = 0 使用0, 0号地址在系统中必定不可分配,而在这里系统就会把其解析为nullptr
  • void*

    • 指向的数据对象可以是任意的数据类型

      1
      2
      3
      4
      int a = 10;
      string n = "sss";
      void* p = &a;
      p = &n;
    • 只能进行地址的存放和比较,不能对它进行数据访问, 例如直接对void*类型的指针进行解引用*p是不被允许的

(2) 指向指针的指针(多级指针)

1
2
3
4
int a = 1;
int* pa = &a;
int** ppa = &pa;
int*** pppa = &ppa

(3) 指向常量的指针

1
2
3
4
const int a = 10; // 常量a, 无法再被修改
const int* p = &a; // 指向常量的指针,解引用*p无法被修改(即无法进行*p = xx的赋值),但是可以将指针指向别处常量
const int b = 11;
p = &b;

(4) 指针常量

1
2
3
4
5
6
int c = 12, d = 13; // 指针常量,只能指向普通变量地址,无法指向常量地址(指针常量中常量的含义是指这个指针的地址是个常量,无法被修改,但是地址所指的内存中存放的数据是可以被修改的。)
int* const p1 = &c;// 指针常量,无法再指向别处(即无法再进行p1 = &b操作), 但是可以将指针所指内容修改,例如
cout << *p1 << endl;
// p1 = &d; 无法进行
*p1 = 14;
cout << *p1 << endl;

(5) 常量指针常量

1
const int* const p = &a; // 指针地址和指针指向的的常量都不能被修改

(6) 指针数组

1
2
int* p = arr; // 普通指针,指向一个数组
int* p_arr[4]; // 指针数组,一个数组,数组中每个元素都是一个int类型指针

(7) 数组指针

1
int(* arr_p)[5]; // 数组指针,一个指针,指向一个int数组,数组包含5个元素

虽然p, arr_p打印出来指向的地址都相同, 但是p的指针移动的步长是一个int类型的长度,而arr_p的步长则是一个长度为5int类型数组的长度。所以两者是不同的,知识他们的首地址相同。如果两者分别进行+1操作,其结果的地址将不同.

1
2
3
4
++p;
++arr_p;
cout << p << endl; //结果:0000007B29CFF59C
cout << arr_p << endl;//结果:0000007B29CFF5A4

1.3 流程控制

  • 顺序
  • 分支
  • 循环
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
#include <iostream>
using namespace std;

int main() {
int count = 0;
if (count == 0) {
cout << "Count is zero." << endl;
} else {
cout << "Count is not zero." << endl;
}

while (count < 5) {
cout << "Count: " << count << endl;
count++;
}

for (int i = 0; i < 5; i++) {
cout << "For Loop Count: " << i << endl;
}

switch(count) {
case 0:
cout << "Count is zero in switch." << endl;
break;
case 5:
cout << "Count is five in switch." << endl;
break;
default:
cout << "Count is neither zero nor five in switch." << endl;
}
}

return:

主函数中的return 0可以省略

1.4 函数

2. 进阶篇

2.1 对象和类

2.2 动态内存

2.3 标准库

2.4 异常处理

3. 高级篇

3.1 运算符重载

3.2 拷贝控制

3.3 面向对象编程

3.4 泛型编程

背景:几年前我通过hexo博客框架自己搭了一套自己的博客,托管在github io服务器上。最近我在博客园也开通了博客,用作backup。

如果手动地同步github io上的文章到博客园,工作量有点大,并且github io文章中引用的图片大部分也在自定义的github图床上,如果github图床出问题,博客园文章也会受影响。

因此我打算将github io上的文章全量同步至博客园,并且将文章中的图片也上传至博客园,同时替换图片引用。

1. 介绍说明

1.1 基础设施的变革

  • 单机场景

单机(操作系统+app)–> 虚拟化(VM+OS+APP) –> 容器化(Container + APP)

  • 集群场景 IAAS

  • 集群场景 PAAS

规则引擎 Drools

1. 简介

业务规则引擎,业务规则管理系统, BRMS。

规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或者开发者在需要时进行配置管理。

规则引擎并不是一个具体的技术框架,而是指一类系统,即业务规则管理系统。市面上常见产品有drools,VisualRules,iLog等

规则引擎实现了将业务规则从程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。

1.1 优点

  • 业务规则和系统代码解耦,实现业务规则的集中管理
  • 在不重启服务的情况下,可随时对业务规则进行扩展和维护(热更新)
  • 可以动态修改业务规则,从而快速响应业务方的需求变更,大大提高了生产效率。

1.2 应用场景

1. 项目概述

1.1 项目介绍

用户权限管理

具有用户、部门、岗位、角色、菜单管理。并通过网关进行统一的权限认证

微服务开发框架

集成了基础的公共组件,包括数据库,缓存,日志,表单验证,对象转换,防注入和接口文档管理等工具

1.2 业务架构

1.3 技术架构

1. Spring Security快速入门

Filter是由Servlet容器加上FilterChain进行管理的,然后SpringSecurity是通过在FilterChain中注册一个Filter的代理对象,然后,再到Spring容器中定义SpringSecurity的FilterChain, 这些spring容器中的FilterChain是通过再Servlet中定义的Filter代理对象进行管理的。

1.1 DefaultSecurityFilterChain

SpringSecurity默认的SecurityFilterChain

1.2 SecurityProperties

定义默认配置

2. Spring Security自定义配置

2.1 创建自定义配置

基础篇

1. 初识MQ

1.1 同步调用

同步调用是一种线性执行模式。当你调用一个函数后,程序会暂停在当前位置,直到这个函数执行完毕并返回结果后,才会继续执行下一行代码。这就像你在餐厅点餐后,站在柜台前一直等到厨师做好餐品拿到手后才离开.

缺点:

  • 拓展性差: 拓展服务需要更改通知代码
  • 性能下降: 串行执行, 效果慢
  • 级联失败: 前面服务失败, 后面服务也失败

使用场景: 下一步操作需要上一步操作的结果才使用同步调用, 否则可优化为异步调用

1.2 异步调用

异步调用是一种非阻塞的执行模式。发出调用后,程序不会傻等,而是立即继续执行后续代码。被调用的函数(或任务)会在后台(例如在另一个线程中)执行,当它完成时,会通过一种通知机制(如回调函数、事件或消息)来告知调用方结果已就绪 。这就好比你在餐厅点餐后,拿到一个取餐号,然后可以回座位玩手机,当餐准备好时,服务员会叫号通知你取餐 .

基础篇

1. 常见数据结构和命令

1.1 五种常见数据结构

1
2
3
4
5
String
Hash
Set
List
SortedSet

1.2 五种扩展数据结构

(1) Redis 位图(Bitmap)

位图本质是数组,它是基于String数据类型的按位的操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(我们称之为一个索引)。
Bitmap支持的最大位数是2^32位,它可以极大的节约存储空间,使用512M内存就可以存储多达42.9亿位信息(2^32=4294967296)

1. 核心功能

1.1 MQ介绍

RocketMQ基本组件

  • Topic: 消息归类的基本单元
  • Queue: 消息队列
  • Producer
  • Consumer
  • ConsumerGroup:
  • NameServer:可以理解为注册中心,负责更新和发现Broker
  • Broker集群:Broker 可以有一个或多个,每一个Brocker就是一个Kafka实例(RacketMQ实例)

1.2 RocketMQ环境搭建

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
#[Step 1] :拉取 RocketMQ 镜像
docker pull apache/rocketmq:5.1.0

# [Step 2] : 创建容器共享网络 rocketmq
docker network create rocketmq

# [Step 3] : 部署 NameServer 前提条件
# 创建目录
mkdir -p /data/rocketmq/nameserver/{bin,logs}

# 授权文件
chmod 777 -R /data/rocketmq/nameserver/*

# 创建容器
docker run -d \
--privileged=true --name rmqnamesrv \
apache/rocketmq:5.1.0 sh mqnamesrv

# 拷贝启动脚本
docker cp rmqnamesrv:/home/rocketmq/rocketmq-5.1.0/bin/runserver.sh /data/rocketmq/nameserver/bin/

# 删除容器 NameServer
docker rm -f rmqnamesrv


# [Step 4] : 启动容器 NameServer
# 启动容器 NameServer
docker run -d --network rocketmq \
--privileged=true --restart=always \
--name rmqnamesrv -p 9876:9876 \
-v /data/rocketmq/nameserver/logs:/home/rocketmq/logs \
-v /data/rocketmq/nameserver/bin/runserver.sh:/home/rocketmq/rocketmq-5.1.0/bin/runserver.sh \
apache/rocketmq:5.1.0 sh mqnamesrv

# 部分命令解释 :
1. -e "MAX_HEAP_SIZE=256M" 设置最大堆内存和堆内存初始大小
2. -e "HEAP_NEWSIZE=128M" 设置新生代内存大小




# [Step 5] : 查看 NameServer 启动日志
# 查看启动日志
docker logs -f rmqnamesrv
# 成功,则进行下一步

# [Step 6] : 部署 Broker + Proxy
mkdir -p /data/rocketmq/broker/{store,logs,conf,bin}
chmod 777 -R /data/rocketmq/broker/*

# [Step 7] : 创建broker.conf文件
vim /data/rocketmq/broker/conf/broker.conf
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
# nameServer 地址多个用;隔开 默认值null
# 例:127.0.0.1:6666;127.0.0.1:8888
namesrvAddr = 192.168.56.2:9876
# 集群名称
brokerClusterName = DefaultCluster
# 节点名称. 主节点的名称必须要和从节点名称一致
brokerName = broker-a
# broker id节点ID, 0 表示 master, 其他的正整数表示 slave,不能小于0
brokerId = 0
# Broker服务地址 String 内部使用填内网ip,如果是需要给外部使用填公网ip
brokerIP1 = 192.168.56.2
# Broker角色
brokerRole = ASYNC_MASTER
# 刷盘方式
flushDiskType = ASYNC_FLUSH
# 在每天的什么时间删除已经超过文件保留时间的 commit log,默认值04
deleteWhen = 04
# 以小时计算的文件保留时间 默认值72小时
fileReservedTime = 72
# 是否允许Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
# 是否允许Broker自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
# 禁用 tsl
tlsTestModeEnable = false

# 下面是没有注释的版本, 记得修改"namesrvAddr", "brokerIP1"的地址
# namesrvAddr = 192.168.56.2:9876
# brokerClusterName = DefaultCluster
# brokerName = broker-a
# brokerId = 0
# brokerIP1 = 192.168.56.2
# brokerRole = ASYNC_MASTER
# flushDiskType = ASYNC_FLUSH
# deleteWhen = 04
# fileReservedTime = 72
# autoCreateTopicEnable=true
# autoCreateSubscriptionGroup=true
# tlsTestModeEnable = false

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
# [Step 8] : 拷贝启动脚本
# 启动 Broker 容器
docker run -d \
--name rmqbroker --privileged=true \
apache/rocketmq:5.1.0 sh mqbroker

# 拷贝脚本文件
docker cp rmqbroker:/home/rocketmq/rocketmq-5.1.0/bin/runbroker.sh /data/rocketmq/broker/bin

# [Step 9] : 启动容器 Broker
# 删除容器 Broker
docker rm -f rmqbroker

# 启动容器 Broker
docker run -d --network rocketmq \
--restart=always --name rmqbroker --privileged=true \
-p 10911:10911 -p 10909:10909 \
-v /data/rocketmq/broker/logs:/root/logs \
-v /data/rocketmq/broker/store:/root/store \
-v /data/rocketmq/broker/conf/broker.conf:/home/rocketmq/broker.conf \
-v /data/rocketmq/broker/bin/runbroker.sh:/home/rocketmq/rocketmq-5.1.0/bin/runbroker.sh \
-e "NAMESRV_ADDR=rmqnamesrv:9876" \
apache/rocketmq:5.1.0 sh mqbroker --enable-proxy -c /home/rocketmq/broker.conf

# 查看启动日志
docker logs -f rmqbroker


# [Step 10] : 部署RocketMQ控制台(rocketmq-dashboard)
docker pull apacherocketmq/rocketmq-dashboard:latest


# [Step 11] : 启动容器 Rocketmq-dashboard
docker run -d \
--restart=always --name rmq-dashboard \
-p 8080:8080 --network rocketmq \
-e "JAVA_OPTS=-Xmx256M -Xms256M -Xmn128M -Drocketmq.namesrv.addr=rmqnamesrv:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
apacherocketmq/rocketmq-dashboard

# 日志
docker logs -f rmq-dashboard

整合springboot