Guitar 🎸

指板

chords
在乐音体系中,七个具有独立名称的音级叫做基本音级(也叫自然音级)。 基本音级用“C、D、E、F、G、A、B”七个字母来表示,这就是基本音级的音名唱名是人们在演唱音乐的谱子时所使用的名称,即:“Do,Re,Mi,Fa,Sol,La,Si” 这七个唱名。
简谱上 唱名、音名的表示
十二平均律,是将一个八度的音程等分成十二个半音的律制,各相邻两律之间的波长之比完全相等。一等份为一个半音(小二度),对应吉他一品的距离。两等份为一个全音(大二度)。将一个八度分成12等份有着惊人的一些巧合,这是因为它的纯五度音程的两个音的波长比为(1/2)^(7/12)≈0.6674,与2/3≈0.6667非常接近。 一个八度内 全全半全全全半

C大调音阶在吉他指板上可以有不同的把位 https://www.zhihu.com/tardis/zm/art/496745355?source_id=1005
C大调音阶图

扫弦节奏型:1)下 下 下上 上下上下 下上
节拍

乐理

HUB GUITAR: https://hubguitar.com/zh_han/music-theory 指板: https://hubguitar.com/zh_han/fretboard

弹奏第一品位上的B弦,这就是 C音,一个以一定频率振动的音波。接下来你拨第十三品的同一根弦,同样地,它听上去好像是同一个音,但这个音高更高。这是因为声波的振动是原来的两倍之快,可以表达为1:2, 它们是有同一特性的音。如果两种不同的乐器同时弹奏C音,这个比例是1:1,这称为 同音。在下图中,第一品的音符将会是最左边的C,第十三品的音则是在最右边。所有在这两者之间的音符都是特定的音符,但一旦重新回到C音,就重新按照这个顺序继续下去。

音程:就是两个音符之间的高低关系。在较低的一个 “C” 音和另一个较高的 “C” 音之间的音程就是 八度(八音音阶)。八度是音高的基本来源,其余的还包括将它分成更小的部分而得到的音高,称为 音数。半音就是移动一格,从 C 到 C♯。全音移动两格,从 C 到 D。 大多数现代音乐将八度分割为12级,如图所示,你可以按照这个音符的顺序来弹奏,从第一品的 B 弦开始,每次移动一个品位,直到第十三品,重新回到 C,一边弹奏一边大声说出这个音符的名字。所有这十二个音符一起组成了 半音音阶。音阶就是音符的顺序,并且没有重复的音符,所有的音符以升序,从低到高的顺序来弹奏。

根音?它就是一首歌的主调音,它是一个单音,其余的东西全在它的基础上变化,想像它是重力中心,它是一种吸引力,吸引着一首歌曲里其余所有的音,不管什么情况下都会回到根音上来。

TABS


晴天 - 周杰倫

Hey Jude - The Beatles

小情歌 - 蘇打綠 https://www.bilibili.com/video/BV1R64y1p7WB/

紅豆 - 方大同

找自己 - 陶喆

普通朋友 - 陶喆 https://www.bilibili.com/video/BV1zw4m1a7CU

Canon in C https://www.bilibili.com/video/BV1if4y1A7SZ/

揪心的玩笑與漫長的白日夢 - 萬能青年旅店
https://www.bilibili.com/video/BV1cuHjetENY/?vd_source=ff210768dfaee27c0d74f9c8c50d7274

Kubernetes

2023-07-31 10:25:07 Docker /

Docker 是一种开源平台,一种快速构建、运行和管理应用的工具。它使用容器化技术,使得应用程序及其依赖性可以打包到一个容器中,并在任何支持 Docker 的环境中运行。
1 MobarXterm 通过 SSH 连接 linux虚拟机,操作虚拟机上的 Docker。
2 Windows本地:通过wsl安装Linux发行版本,安装docker desktop(将自动在WSL中配置Docker环境,借助linux内核运行)

容器(Container)

  • 容器是一个轻量级的、可移植的、自包含的单元,包括应用程序和其所有依赖项。
  • Docker 利用容器技术,将应用程序及其依赖项打包成一个容器,确保在不同环境中的一致性运行。
  • Docker 位于容器 和 服务器-操作系统/硬件 之间,是运行容器的引擎。
  • 隔离网络、文件、进程等环境。一个容器是一个沙盒隔离环境。
  • 相对于虚拟机技术,docker 启动更快、更清量。但容器共用宿主机的内存、CPU物理资源,多容器可能存在互相抢占资源的情况。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    docker run -d \                  # 创建并运行一个容器,-d 是让容器在后台运行;同一个镜像可创建多个容器
    --name mysgl \ # 给容器起名字,必须唯一
    -p 3306:3306 \ # 设置 宿主机端口:容器端口 映射
    -e TZ=Asia/Shanghai \ # 设置环境变量
    -e MYSQL_ROOT_PASSWORD=123 \ # 指定运行的镜像名,一般为 [镜像名]:[镜像版本]
    mysql
    docker ps # 查看本地容器(运行中的)
    docker ps a # 查看所有容器 (包括未运行)
    docker start <容器ID> # 启动容器
    docker stop <容器ID> # 停止容器中的进程,容器未删除
    docker rm <容器ID> # 删除容器
    docker inspect <容器ID> # 查看容器配置信息
    docker log <容器ID> # 查看容器日志
    docker exec -it <容器ID> bash # 进入容器内部,命令行模式(容器内部模拟出一个操作系统)
    容器打包成镜像: docker commit -a "作者信息" -m "log信息" <容器ID><目标镜像名称:tag版本>
    拷贝文件到容器: docker cp <文件目录> <容器ID>:<目标目录>
    拷贝容器文件到宿主机:docker cp <容器ID>:<文件目录><宿主机目标目录>
    更新容器设置:docker update <容器ID><相关设置>

镜像(Image)

  • 镜像是一个只读的模板,包含运行应用程序所需的所有信息,包括代码、运行时、库、环境变量和配置文件。
  • 容器是通过运行镜像创建的(像光盘),本地容器是真正运行的实例。镜像是容器的模板,是从容器打包来的,可以在不同操作系统,不同服务器之间传播。
  • 1
    2
    3
    4
    5
    6
    7
    docker images                                # 查看本地镜像
    docker search <名称关键字> # 搜索镜像仓库
    docker pull <镜像名:tag版本> # 下载镜像
    docker push <镜像名:tag版本> # 上传镜像
    docker rmi <镜像名:tag版本> # 删除镜像
    docker save -o <输出文件路径><镜像名:tag版本> # 打包本地镜像文件
    docker load -i <加载文件路径 # 导入本地镜像文件

仓库(Registry)

  • 仓库是存储和组织 Docker 镜像的地方。Docker Hub 是一个常见的公共仓库,你也可以搭建私有仓库。
  • Docker 镜像可以从仓库中拉取,也可以推送到仓库。

沙箱

  • 沙箱是一种安全机制,用于隔离和限制程序或应用程序的运行环境,以防止其对系统或其他程序产生潜在的危害。沙箱技术旨在创建一个受控制的环境,使得运行在其中的代码无法直接影响到系统的其他部分。这种隔离有助于确保安全性、防止恶意软件传播,同时提供一定程度的控制和监控。
  • 容器化平台(如 Docker)使用沙箱技术来隔离容器中的应用程序,确保它们互相独立运行。

容器创建

镜像结构:入口,层,基础镜像。分层的好处是可复用,,

  • 通过命令直接创建,需要完整镜像,几个G常有,稳定。
  • 通过dockerfile创建,不需要完整镜像,更灵活。
    Dockerfile 是一个包含构建镜像步骤的文本文件,包含一个个的指令。通过编写 Dockerfile,你可以定义如何构建镜像,包括基础镜像、安装依赖、复制文件等步骤。
  • 使用 Docker 的基本步骤
    1. 安装 Docker: 根据操作系统的不同,安装适合的 Docker 版本。
    2. 创建 Dockerfile: 编写包含应用程序构建步骤的 Dockerfile。
    3. 构建镜像: 在包含 Dockerfile 的目录中运行 docker build 命令构建镜像。(如java项目还需要jar包)
    4. 运行容器: 使用 docker run 命令基于构建的镜像创建和运行容器。
    5. 发布镜像: 将构建的镜像推送到 Docker 仓库,以便其他人可以拉取使用。

数据卷

  • 数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁(两边文件同时修改)。
    (容器一般只包括支持运行的最少文件,一般无vi或其他编辑器,所以无法进入容器直接对容器中的文件进行修改)
  • 如何挂载数据卷?在创建容器时,利用-v 数据卷名:容器内目录完成挂载。创建时如果发现挂载的数据卷不存在,会自动创建。
    1
    2
    3
    4
    docker volumels        # 查看数据卷
    docker volume rm # 删除数据卷
    docker volume inspect # 查看数据卷详情
    docker volume prune # 删除未使用的数据卷

容器编排(Orchestration):运维人员

容器编排是指在生产环境中管理和协调多个容器的过程。Docker 提供了 Docker Compose 工具,用于定义和运行多容器的应用。


Kubernetes

1. 什么是Kubernetes

Kubernetes是一个开源的容器编排引擎,可以用来管理容器化的应用,包括容器的自动化的部署、扩容、缩容、升级、回滚等等;
它是Google在2014年开源的一个项目,它的前身是Google内部的Borg系统。

Kubernetes出现之前,我们一般都是使用Docker来管理容器化的应用,但是Docker只是一个单机的容器管理工具,它只能管理单个节点上的容器,当我们的应用程序需要运行在多个节点上的时候,就需要使用一些其他的工具来管理这些节点,比如Docker Swarm、Mesos、Kubernetes等等;
这些工具都是容器编排引擎,它们可以用来管理多个节点上的容器,但是它们之间也有一些区别,比如Docker Swarm是Docker官方提供的一个容器编排引擎,它的功能比较简单,适合于一些小型的、简单的场景,而Mesos和Kubernetes则是比较复杂的容器编排引擎;
Mesos是Apache基金会的一个开源项目,而Kubernetes是Google在2014年开源的,目前已经成为了CNCF(Cloud Native Computing Foundation)的一个顶级项目,基本上已经成为了容器编排引擎的事实标准了。

2.1 Kubernetes 资源对象

Node:k8s集群节点,可以是物理机/虚拟机
Pod:k8s最小调度单元,容器(运行app/数据库/..镜像)的抽象,可以是一/多个容器的组合,但除非高度耦合,一个pod只运行一个容器
Service:将一组pod封装成一个服务并且提供统一访问入口(解决了一组数据库pod中一个重建后ip变化的问题,类似于“服务发现”)
Ingress:为了对外提供服务,将外部请求路由转发到内部集群的service上

ConfigMap:封装配置信息
Secret:封装敏感信息
其他安全机制:网络安全,访问控制,身份认证
Volumn:将数据挂在到本地磁盘或远程存储上,实现持久化存储

Deployment:部署无状态应用程序,将一/多个Pod组合到一起;冗余备份,相当于对Pod的抽象;具有副本控制、滚动更新、自动扩缩容等功能,实现应用程序的高可用
Statefulset:部署有状态应用程序,如DB、MQ、缓存以及保留会话状态的应用程序

2.2 Kubernetes 架构

分为Master和Worker节点
apiserver:位于master节点上,是k8s集群的API接口;交互方式包括 kubectl 命令行、Dashboard界面或API接口

3. 使用minikube搭建kubernetes集群环境

minikube是一个轻量级的kubernetes集群环境,可以用来在本地快速搭建一个单节点的kubernetes集群;
https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/ 你好,Minikube

4. 使用Multipassk3s搭建kubernetes集群环境

minikube只能用来在本地搭建一个单节点的kubernetes集群环境,
下面介绍如何使用Multipassk3s来搭建一个多节点的kubernetes集群环境,

5. 在线实验环境

Killercoda Play-With-K8s

6. kubectl常用命令

6.1 基础使用

1
2
3
4
5
6
7
8
# 查看帮助
kubectl --help

# 查看API版本
kubectl api-versions

# 查看集群信息
kubectl cluster-info

6.2 资源的创建和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建并运行一个指定的镜像
kubectl run NAME --image=image [params...]
# e.g. 创建并运行一个名字为nginx的Pod
kubectl run nginx --image=nginx

# 根据YAML配置文件或者标准输入创建资源
kubectl create RESOURCE
# e.g.
# 根据nginx.yaml配置文件创建资源
kubectl create -f nginx.yaml
# 根据URL创建资源
kubectl create -f https://k8s.io/examples/application/deployment.yaml
# 根据目录下的所有配置文件创建资源
kubectl create -f ./dir

# 通过文件名或标准输入配置资源
kubectl apply -f (-k DIRECTORY | -f FILENAME | stdin)
# e.g.
# 根据nginx.yaml配置文件创建资源
kubectl apply -f nginx.yaml

6.3 查看资源信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看集群中某一类型的资源
kubectl get RESOURCE
# 其中,RESOURCE可以是以下类型:
kubectl get pods / po # 查看Pod
kubectl get svc # 查看Service
kubectl get deploy # 查看Deployment
kubectl get rs # 查看ReplicaSet
kubectl get cm # 查看ConfigMap
kubectl get secret # 查看Secret
kubectl get ing # 查看Ingress
kubectl get pv # 查看PersistentVolume
kubectl get pvc # 查看PersistentVolumeClaim
kubectl get ns # 查看Namespace
kubectl get node # 查看Node
kubectl get all # 查看所有资源

# 后面还可以加上 -o wide 参数来查看更多信息
kubectl get pods -o wide

# 查看某一类型资源的详细信息
kubectl describe RESOURCE NAME
# e.g. 查看名字为nginx的Pod的详细信息
kubectl describe pod nginx

6.4 资源的修改、删除和清理

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
# 更新某个资源的标签
kubectl label RESOURCE NAME KEY_1=VALUE_1 ... KEY_N=VALUE_N
# e.g. 更新名字为nginx的Pod的标签
kubectl label pod nginx app=nginx

# 删除某个资源
kubectl delete RESOURCE NAME
# e.g. 删除名字为nginx的Pod
kubectl delete pod nginx

# 删除某个资源的所有实例
kubectl delete RESOURCE --all
# e.g. 删除所有Pod
kubectl delete pod --all

# 根据YAML配置文件删除资源
kubectl delete -f FILENAME
# e.g. 根据nginx.yaml配置文件删除资源
kubectl delete -f nginx.yaml

# 设置某个资源的副本数
kubectl scale --replicas=COUNT RESOURCE NAME
# e.g. 设置名字为nginx的Deployment的副本数为3
kubectl scale --replicas=3 deployment/nginx

# 根据配置文件或者标准输入替换某个资源
kubectl replace -f FILENAME
# e.g. 根据nginx.yaml配置文件替换名字为nginx的Deployment
kubectl replace -f nginx.yaml

6.5 调试和交互

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
# 进入某个Pod的容器中
kubectl exec [-it] POD [-c CONTAINER] -- COMMAND [args...]
# e.g. 进入名字为nginx的Pod的容器中,并执行/bin/bash命令
kubectl exec -it nginx -- /bin/bash

# 查看某个Pod的日志
kubectl logs [-f] [-p] [-c CONTAINER] POD [-n NAMESPACE]
# e.g. 查看名字为nginx的Pod的日志
kubectl logs nginx

# 将某个Pod的端口转发到本地
kubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]
# e.g. 将名字为nginx的Pod的80端口转发到本地的8080端口
kubectl port-forward nginx 8080:80

# 连接到现有的某个Pod(将某个Pod的标准输入输出转发到本地)
kubectl attach POD -c CONTAINER
# e.g. 将名字为nginx的Pod的标准输入输出转发到本地
kubectl attach nginx

# 运行某个Pod的命令
kubectl run NAME --image=image -- COMMAND [args...]
# e.g. 运行名字为nginx的Pod
kubectl run nginx --image=nginx -- /bin/bash

7. Portainer的安装和使用

Portainer 是一个轻量级的容器管理工具,可以用来管理Docker和Kubernetes,它提供了一个Web界面来方便我们管理容器
官方网址: https://www.portainer.io/

8. Helm的安装和使用

Helm 是一个Kubernetes的包管理工具,可以用来管理Kubernetes的应用,它提供了一个命令行工具来方便我们管理Kubernetes的应用
官方网址: https://helm.sh/

集群存储

https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/

InfluxDB

InfluxDB 是一个由InfluxData开发的开源时序型数据库,专注于海量时序数据的高性能读、高性能写、高效存储与实时分析等;
在DB-Engines Ranking时序型数据库排行榜上排名第一,广泛应用于DevOps监控、IoT监控、实时分析等场景。
https://jasper-zhang1.gitbooks.io/influxdb/content/Introduction/getting_start.html

influxdb-cluster 是InfluxDB的集群版本,InfluxDB Enterprise 的开源替代方案,设计用于大规模数据存储和高可用性需求。
可以实现数据的分片和复制,从而提高系统的可用性和扩展性。数据安全。operator缺失
https://github.com/chengshiwen/influxdb-cluster/wiki

集群体系结构

InfluxDB Enterprise由两组软件进程组成: Data 数据节点 和 Meta 元节点。集群内的通信是这样的:

influxdb使用的默认端口号为分别为用于meta集群内部服务的8091端口,meta节点通信的8089端口,data集群内部服务的8088端口,以及data节点对外提供http服务的8086端

  • Meta 节点通过 TCP 协议和 Raft 共识协议相互通信,默认都使用端口 8089,此端口必须在 Meta 节点之间是可访问的。默认 Meta 节点还将公开绑定到端口 8091 的 HTTP API,influxd-ctl 命令使用该 API。
  • Data 节点通过绑定到端口 8088 的 TCP 协议相互通信。Data 节点通过绑定到 8091 的 HTTP API 与 Meta 节点通信。这些端口必须在 Meta 节点和 Data 节点之间是可访问的。
  • 在集群内,所有 Meta 节点都必须与所有其它 Meta 节点通信。所有 Data 节点必须与所有其它 Data 节点和所有 Meta 节点通信。

Where data lives

InfluxDB 集群中,一个节点要么是专门用于存储和查询时间序列数据的数据节点,要么是专门用于存储集群元数据的元节点。数据节点负责存储实际的数据和处理查询请求,而元节点则负责管理集群的元数据,包括节点信息、数据库和保留策略等。

Meta

元节点保存以下所有元数据:

  • 集群中的所有节点及其角色
  • 集群中存在的所有数据库和保留策略
  • 所有分片和分片组,以及它们存在于哪些节点上
  • 集群用户及其权限
  • 所有连续查询

元节点将这些数据保存在磁盘上的Raft数据库中,由BoltDB提供支持。默认情况下,Raft数据库是/var/lib/influxdb/meta/raft.db。
注意:Meta节点需要/ Meta目录。

  • influxd-meta 元数据服务
    1
    2
    3
    4
    5
    # 配置文件示例(meta节点)
    [meta]
    dir = "/var/lib/influxdb/meta" # 元数据存储路径
    bind-address = ":8089" # Raft协议通信端口
    http-bind-address = ":8091" # 管理API端口
  • influxd-ctl 集群管理
    1
    2
    3
    4
    5
    6
    7
    8
    # 查看分片分布
    kubectl exec influxdb-meta-0 -- influxd-ctl show-shards
    # 强制同步分片到新节点
    kubectl exec influxdb-meta-0 -- influxd-ctl copy-shard 3 influxdb-data-0:8088
    # 节点维护操作
    influxd-ctl remove-data influxdb-data-0:8088 # 下线节点
    # 检查Meta节点Raft状态
    kubectl exec influxdb-meta-0 -- influxd-ctl raft-state

Data

数据节点保存所有原始时间序列数据和元数据,包括:

  • measurements
  • tag keys and values
  • field keys and values

在磁盘上,数据总是按照//组织。默认父目录为/var/lib/influxdb/data。注意:数据节点需要/var/lib/influxdb/的所有四个子目录,包括/meta(specifically, the clients.json file)、/data、/wal和/hh。

  • influx CLI工具
    1
    2
    3
    4
    5
    6
    7
    # 进入容器执行CLI
    kubectl exec -it influxdb-data-0 -n influxdb -- influx -username admin -password 'xxx'

    # 常用命令
    SHOW DATABASES; # 显示所有数据库
    SELECT * FROM cpu; # 查询数据
    CREATE RETENTION POLICY "1d" ON db3 DURATION 1d REPLICATION 2; # 创建保留策略
  • influxd 数据节点服务
    1
    2
    3
    4
    5
    6
    # 查看运行状态
    kubectl exec influxdb-data-0 -- ps aux | grep influxd

    # 关键参数
    -data-dir /var/lib/influxdb/data # 数据存储目录
    -wal-dir /var/lib/influxdb/wal # WAL日志目录
  • influx_inspect 数据工具
    1
    2
    3
    4
    5
    6
    7
    8
    # 导出TSM文件(需进入容器)
    kubectl exec -it influxdb-data-0 -- influx_inspect export \
    -datadir /var/lib/influxdb/data \
    -waldir /var/lib/influxdb/wal \
    -out backup.gz -compress

    # 验证数据完整性
    influx_inspect verify -dir /var/lib/influxdb/data/db3

Data 与 Meta节点交互机制

  1. 通信协议

    组件 端口 用途 协议
    Meta节点间 8089 Raft协议同步元数据 TCP
    Data节点间 8088 分片数据复制 TCP
    Data→Meta节点 8091 注册节点/获取分片元信息 HTTP
  2. 核心交互场景
    节点注册 : Data节点启动时通过HTTP API向Meta节点注册(POST /data
    分片分配 : Meta节点根据replication-factor策略分配分片到Data节点
    写入协调 : 客户端写入数据时,由Meta节点确定目标分片所在Data节点
    故障转移 : Meta节点检测Data节点离线后,自动通过Hinted Handoff机制转移副本

一个集群至少要有三个独立的元节点才能允许一个节点的丢失,如果要容忍n个节点的丢失则需要2n+1个元节点。集群的元节点的数目应该为奇数。不要是偶数元节点,因为这样在特定的配置下会导致故障。
一个集群运行只有一个数据节点,但这样数据就没有冗余了。这里的冗余通过写数据的RP中的副本个数来设置。一个集群在丢失n-1个数据节点后仍然能返回完整的数据,其中n是副本个数。为了在集群内实现最佳数据分配,我们建议数据节点的个数为偶数。

术语 / Glossary

  • measurement:描述了存在关联field中的数据的意义,measurement是字符串。作为tag,fields和time列的容器。相当于MySQL的table,关系/表的意思。单个measurement可以有不同的retention policy(即 一个measurement 中的不同 tag set 可以有不同的 retention policy,构成多组 series)
  • Continuous Query (CQ)是在数据库内部自动周期性跑着的一个InfluxQL的查询,CQs需要在SELECT语句中使用一个函数,并且一定包括一个GROUP BY time()语句。
  • Retention Policy (RP)是InfluxDB数据结构的一部分,描述了InfluxDB保存数据的长短,数据存在集群里面的副本数,以及shard group的时间范围。RPs在每个database里面是唯一的,?连同measurement和tag set定义一个series。当创建一个database时,InfluxDB会自动创建一个叫做autogen的retention policy,其duration为永远,replication factor为1,shard group的duration设为七天。
    • duration:决定InfluxDB中数据保留多长时间。在duration之前的数据会自动从database中删除掉。
    • replication factor:决定在集群模式下数据的副本的个数。InfluxDB在N个数据节点上复制数据,其中N就是replication factor。
    • shard group duration决定了每个shard group跨越多少时间。具体间隔由retention policy中的SHARD DURATION决定。例如,如果retention policy的SHARD DURATION设置为1w,则每个shard group将跨越一周,并包含时间戳在该周内的所有点。
  • series:InfluxDB数据结构的集合,一个特定的series由measurement,tag set和retention policy组成。!field set不是series的一部分
  • schema:数据在InfluxDB里面怎么组织。InfluxDB的schema的基础是database,retention policy,series,measurement,tag key,tag value以及field keys。
  • shard:包含实际的编码和压缩数据,并由磁盘上的TSM文件表示。 每个shard都属于唯一的一个shard group。多个shard可能存在于单个shard group中。每个shard包含一组特定的series。给定shard group中的给定series上的所有点将存储在磁盘上的相同shard(TSM文件)中。
  • shard group:是shard的逻辑组合。shard group由时间和retention policy组织。包含数据的每个retention policy至少包含一个关联的shard group。给定的shard group包含其覆盖的间隔的数据的所有shard。每个shard group跨越的间隔是shard的持续时间。

InfluxDB读写

  1. 命令行工具
    • influx命令行连接本地InfluxDB:直接通过InfluxDB的HTTP接口(如果没有修改,默认是8086)来和InfluxDB通信。(说明:也可以直接发送裸的HTTP请求来操作数据库,例如curl)
      1
      2
      3
      4
      $ influx -precision rfc3339
      Connected to http://localhost:8086 version 1.2.x //
      InfluxDB shell 1.2.x
      >
      InfluxDB的HTTP接口默认起在8086上,所以influx默认也是连的本地的8086端口。-precision参数表明了任何返回的时间戳的格式和精度,如 rfc3339是让InfluxDB返回RFC339格式(YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ)的时间戳。
    • 数据格式:将数据点写入InfluxDB,只需要遵守如下的行协议:
      1
      <measurement>[,<tag-key>=<tag-value>...] <field-key>=<field-value>[,<field2-key>=<field2-value>...] [unix-nano-timestamp]
      InfluxDB里存储的数据被称为时间序列数据,其包含一个数值。时序数据有零个或多个数据点,每一个都是一个指标值。数据点包括time(一个时间戳),measurement(例如cpu_load),至少一个k-v格式的field(也即指标的数值例如 “value=0.64”或者“temperature=21.2”),零个或多个tag,其一般是对于这个指标值的元数据(例如“host=server01”, “region=EMEA”)。
      可以将measurement类比于SQL里的table,其主键索引总是时间戳。tag和field是在table里的其他列,tag是被索引起来的,field没有。不同之处在于InfluxDB里,你可以有几百万的measurements,不用事先定义数据的scheme,且null值不会被存储。
    • 使用CLI插入单条的时间序列数据到InfluxDB中,用INSERT后跟数据点:
      1
      2
      3
      > use testdb
      Using database testdb
      > INSERT cpu,host=serverA,region=us_west value=0.64
      这样一个measurement为cpu,tag是host和region,value值为0.64的数据点被写入了InfluxDB中。
    • 现在我们查出写入的这笔数据:
      1
      2
      3
      4
      5
      6
      7
      > SELECT "host", "region", "value" FROM "cpu"
      name: cpu
      ---------
      time host region value
      2015-10-21T19:28:07.580664347Z serverA us_west 0.64

      > delete FROM "cpu" WHERE "host" = 'serverA' # 不带where将删除measurement所有数据
      我们在写入的时候没有包含时间戳,当没有带时间戳的时候,InfluxDB会自动添加本地的当前时间作为它的时间戳。
  2. HTTP 请求
    • 写数据
      1
      curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000'
    • 读数据
      1
      curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=mydb" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'"
  3. 客户端库,InfluxDB 提供了多种编程语言的客户端库,如Python、Go、Java等,可以方便地在应用程序中读写数据。
  4. 采样和数据保留 https://jasper-zhang1.gitbooks.io/influxdb/content/Guide/downsampling_and_retention.html

集群读写

  • 分片Shard:InfluxDB集群读写的基本单位分片是时间序列数据的物理存储单位,每个分片包含一段时间范围内的数据。
    复制因子为X,则在每个分片的数据同步到X个节点(部署influxdb的主机)上。
    分片的划分依据是时间范围和数据的存储策略(Retention Policy)。在集群环境中,分片可以分布在不同的节点上,以实现数据的分布式存储和负载均衡。这样可以提高数据的读写性能和系统的可扩展性。
    确定数据属于哪个分片的过程主要涉及以下几个步骤:数据写入时,首先根据时间戳确定属于哪个Shard Group(分片组)。然后,基于Measurement和Tag的值计算哈希值。最后,根据哈希值将数据分配到具体的分片。

    shard := shardGroup.shards[fnv.New64a(key) % len(shardGroup.Shards)]

  • 分片组Shard groups:集群在一个分片组内创建分片,以最大限度地利用数据节点的数量。
    分片数计算:当集群有 N 个数据节点且副本因子为 X 时,每个分片组中会创建 floor(N/X) 个分片(向下取整)
    示例:若集群有 4 个数据节点,副本因子为 2,则每个分片组包含 4/2=2 个分片(每个分片在 2 个节点上复制)
  • 集群写入:
    假设一个HTTP写操作被发送到服务器D,数据属于分片1。写操作需要被复制到分片1的所有者:数据节点A和B。当写操作进入D时,该节点从其亚转移的本地缓存中确定需要将写操作复制到A和B,并立即尝试对两者进行写操作。
    每个对HTTP API的请求都可以通过一致性查询参数指定一致性级别。 https://docs.influxdata.com/enterprise_influxdb/v1/concepts/clustering/#write-consistency
  • 集群查询:
    根据查询的时间段和数据的复制因子进行分布的。例如,如果保留策略的复制因子为4,则接收查询的协调数据节点将随机选择存储该分片副本的4个数据节点中的任何一个来接收查询。如果我们假设系统的分片持续时间为一天,那么对于查询覆盖的每一天,协调节点都会选择一个数据节点来接收当天的查询。
    协调节点尽可能在本地执行和完成查询。如果一个查询必须扫描多个shard组(在上面的例子中是多个天),协调节点将查询转发给其他节点,以查找本地没有的shard。查询与扫描自己的本地数据并行转发。查询被分发到尽可能多的节点,以查询每个分片组一次。当结果从每个数据节点返回时,协调数据节点将它们组合成返回给用户的最终结果。
  • Shard Group 与 Shard 的实战示例:
    1. 场景描述,假设有以下配置:
      Retention Policy: Duration: 30d | Replication Factor: 2 | Shard Group Duration: 1d,
      集群数据节点: 4 个(A/B/C/D)
    2. 分片组创建
      时间划分:每天 00:00 自动创建新的分片组(如 2025-04-02 ~ 2025-04-03)
      分片数:4/2=2 个分片(Shard 1 & 2)
    3. 分片分布
      分片 1 | 副本节点 A, B | 存储内容 所有哈希值模2=0的 Series Key 数据
      分片 2 | 副本节点 C, D | 存储内容 所有哈希值模2=1的 Series Key 数据
    4. 数据写入示例
      当写入 cpu,host=svr1 usage=80:Series Key = cpu,host=svr1,哈希值模2=1 ⇒ 分片2,数据同时写入节点 C 和 D
    5. 数据查询流程
      查询 SELECT * FROM cpu WHERE time > '2025-04-02':定位到 2025-04-02 分片组, 协调节点同时向 A/B(分片1)和 C/D(分片2)发起查询, 合并结果后返回

集群部署

Docker安装操作单例InfluxDB https://www.cnblogs.com/nhdlb/p/16409849.html
Docker快速开始集群InfluxDB https://github.com/chengshiwen/influxdb-cluster/wiki#docker-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B

在使用容器多节点部署InfluxDB时,数据库、容器、Docker、主机和Kubernetes(k8s)之间的关系可以理解如下:

  • 数据库(InfluxDB):InfluxDB是一个时序数据库,用于存储和查询时间序列数据。在多节点部署中,InfluxDB可以运行在多个容器中,以实现高可用性和负载均衡。
  • 容器:容器是一个轻量级、独立的运行环境,用于打包和运行应用程序及其依赖项。InfluxDB可以被打包成一个容器镜像,并在多个容器实例中运行。
  • Docker:Docker是一个容器化平台,用于创建、部署和管理容器。Docker负责启动和管理运行InfluxDB的容器。
  • 主机:主机是运行Docker和容器的物理或虚拟机器。在多节点部署中,可能有多个主机,每个主机上运行一个或多个InfluxDB容器。
  • Kubernetes(k8s):一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。Kubernetes可以管理多个主机上的容器,提供服务发现、负载均衡、自动扩展和自愈能力。在多节点部署中,Kubernetes可以管理InfluxDB容器的部署,确保它们在多个节点上运行,并提供高可用性和扩展性。
  • 关系总结:InfluxDB 作为数据库运行在 容器 中。容器 由 Docker 创建和管理。Docker 运行在 主机 上。Kubernetes 管理多个 主机 上的 Docker 容器,提供编排和管理功能。通过这种方式,InfluxDB可以在一个分布式环境中高效运行,利用Kubernetes的编排能力实现自动化管理和扩展。

Kubernetes 存储与 InfluxDB Shard 的关系解析

  1. PV/PVC:是 Kubernetes 管理存储资源的抽象层。PV 描述物理存储资源(如 NFS、云盘等),PVC 是 Pod 对存储资源的请求声明。PVC 绑定到 PV 后,Pod 通过挂载 PVC 使用持久化存储。
  2. Shard:是 InfluxDB 存储引擎的物理存储单元,表现为磁盘上的 TSM 文件(Time-Structured Merge Tree),每个 Shard 对应一个时间范围内的时序数据块。Shard 的存储路径通常位于 PVC 挂载的 /var/lib/influxdb/data 目录下。
    每个 Shard 包含:时间序列索引(.tsi 文件),压缩后的时序数据块(.tsm 文件),WAL(Write-Ahead Log)日志文件(.wal) 其路径结构为:/var/lib/influxdb/data/<database>/<retention_policy>/<shard_id>
  3. 重建 PVC 导致数据丢失的本质问题
    PVC 删除与 PV 回收策略:若 PVC 的回收策略为 Delete(默认),删除 PVC 会导致 Kubernetes 清理其绑定的 PV 及底层存储数据(如 NFS 目录、云盘等)。此时 /var/lib/influxdb 下的 datameta 目录被清空,导致 Shard 文件丢失。
    • Shard 文件丢失会导致对应时间范围的时序数据不可查询,触发 ERR: shard not found 错误。
    • Meta 文件丢失会破坏集群元数据一致性,导致用户权限、分片策略等配置失效。

InfluxDB 备份与恢复

https://docs.influxdata.com/enterprise_influxdb/v1/administration/backup-and-restore/
https://blog.csdn.net/weixin_46560589/article/details/127748939

InfluxDB Enterprise支持在集群实例、单个数据库和保留策略以及单个分片中备份和恢复数据。

  1. 备份整个实例,即所有数据库(全量备份)。命令如下:
    1
    influxd backup -portable /path/to/backup
  2. 备份单个数据库
    1
    influxd backup -portable -database <database_name> /path/to/backup
  3. 增量备份:对于较大的数据集,可以进行增量备份,只备份自上次全量或增量备份以来的数据
    1
    influxd backup -portable -start <timestamp> /path/to/backup

备份的数据可以恢复到新实例或现有实例中。

  1. 恢复整个实例,包括所有的数据库。命令如下:
    1
    influxd restore -portable /path/to/backup
  2. 恢复单个数据库
    1
    influxd restore -portable -db <database_name> /path/to/backup
  3. 有时你可能希望将备份的数据恢复到另一个数据库,可以使用 -newdb 选项来实现:
    1
    influxd restore -portable -db <old_database_name> -newdb <new_database_name> /path/to/backup

导出和导入数据

对于大多数InfluxDB Enterprise应用程序,备份和恢复实用程序提供了备份和恢复策略所需的工具。但是,在某些情况下,标准备份和恢复实用程序可能无法充分处理应用程序中的大量数据。作为标准备份和恢复实用程序的替代方案,可以使用InfluxDB influx_inspect export和涌入-import命令为灾难恢复和备份策略创建备份和恢复过程。

  1. 数据库导出:容器层面命令,指定 数据文件和 写前日志(WAL)文件的存储目录,将指定数据库中指定时间的数据导出到指定文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_test01_dump_out" -database "test01" -start "2024-10-22T00:00:00Z"
    writing out tsm file data for test01/autogen...complete.
    writing out wal file data for test01/autogen...complete.
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# cat influxdb_test01_dump_out
    # INFLUXDB EXPORT: 2024-10-22T00:00:00Z - 2262-04-11T23:47:16Z
    # DDL
    CREATE DATABASE test01 WITH NAME autogen
    # DML
    # CONTEXT-DATABASE:test01
    # CONTEXT-RETENTION-POLICY:autogen
    # writing tsm data
    temp,location=room1 value=24.5 1729589246099070937
    temp,location=room2 value=22.5 1729589253425715740
    temp,location=room3 value=22 1729649010554978701
    # writing wal data
  2. 数据库导入:容器层面执行命令,使用admin账号,指定文件、数据库、时间戳精度
    1
    2
    3
    4
    root@influxdb-e73f149ff7192bd87d190-data-1:/# influx -import -path='influxdb_test01_dump_out' -precision=ns -username='' -password=''
    2024/10/25 03:21:47 Processed 1 commands
    2024/10/25 03:21:47 Processed 2 inserts
    2024/10/25 03:21:47 Failed 0 inserts
  3. 实例导出:把influxdb集群实例中所有数据库的数据导出,不加 -database,加 -compress
  4. 实例导入:加-compressed 导入压缩文件,本质上是先解压后倒入

InfluxDB节点迁移

Data节点迁移方案评审:先迁移后逐个恢复分片数据。已验证在分片副本大小70M、写入数据达2000point/s的情况下直接copy-shard会导致增量数据丢失,考虑在copy-shard前先执行truncate-shards截断热分片(集群中所有写入最新数据的分片,截断后关闭写入,变成冷分片),并在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了全部原有分片的新热分片副本,最新数据写入这个副本,然后再逐个从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复;自测符合预期

  1. 在做迁移操作前,先记录迁移节点拥有的分片副本,后续从健康节点的相同副本中恢复出来;
    1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl show-shards # 或influx命令行执行show shards
  2. 迁移后更新节点/分片元信息,新data-0节点丢失db1的shard3,另外_internal的db1转移到健康节点data-1上了
    1
    2
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl remove-data influxdb-xx-data-0.influxdb-xx-data:8088
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl add-data influxdb-xx-data-0.influxdb-xx-data:8088
  3. 持续写入数据到db1(只能写入健康节点上的db1分片副本),某一时刻执行truncate-shards,db1的shard3切断,新热分片shard4的分片副本分配到data-1以及迁移后的data-0中,此刻开始写入db1的新数据在data-0和data-1上的分片中一致(一个数据库的分片可能有多个,但只有一个正在写入,其他都是冷分片)
    1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl truncate-shards
  4. 最后再恢复历史数据,也就是执行迁移前的data-0节点拥有的分片副本数据,以及迁移完成前应该写入但没有写入data-0的数据。从健康的data-1上的分片副本copy-shard而来,导出文件可见data-0和data-1上的db1数据完全一致
    1
    2
    3
    # 对于_internal分片的转移 先copy后remove
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl copy-shard influxdb-xx-data-1.influxdb-xx-data:8088 influxdb-xx-data-0.influxdb-xx-data:8088 1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl remove-shard influxdb-xx-data-1.influxdb-xx-data:8088 1
    1
    2
    3
    # 分片的物理文件 wal&tsm
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- ls /var/lib/influxdb/data
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- ls /var/lib/influxdb/wal
    1
    2
    3
    # 可能要等wal落tsm
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_dump_out" -database "db1"
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- md5sum influxdb_dump_out

Golang


Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。
Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。 对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。

除了OOP外,近年出现了一些小众的编程哲学,Go语言对这些思想亦有所吸收。例如,Go语言接受了函数式编程的一些想法,支持匿名函数与闭包。再如,Go语言接受了以Erlang语言为代表的面向消息编程思想,支持goroutine和通道,并推荐使用消息而不是共享内存来进行并发编程。总体来说,Go语言是一个非常现代化的语言,精小但非常强大。

Go

  • Go 语言最主要的特性
    自动垃圾回收 更丰富的内置类型 函数多返回值 错误处理 匿名函数和闭包 类型和接口 并发编程 反射 语言交互性
    https://www.runoob.com/go/go-tutorial.html
  • Go 和 Java 有很多共同之处
    C 系列 (强类型,括号) 静态类型 垃圾收集 内存安全 (nil 引用,运行时边界检查) 变量总是初始化 (zero/nil/false)
    方法 接口 类型断言 (实例) 反射
  • Go 与 Java 的不同之处
    代码程序直接编译成机器码,没有 VM
    静态链接二进制 内存布局控制 函数值和词法闭包 内置字符串 (UTF-8) 内置泛型映射和数组/片段 内置并发
  • Go 特意去掉了大量的特性
    没有类 没有构造器 没有继承 没有 final 没有异常 没有注解 没有自定义泛型
  • 为 Java 程序员准备的 Go 语言入门 PPT https://www.runoob.com/w3cnote/go-for-java-programmers.html

Go 项目构建

  • Module:是 Go 语言用于管理项目依赖和版本的一种机制。go.mod 文件定义了模块的元数据和依赖关系。
    go.mod 文件是声明依赖的地方,记录了项目的依赖关系。如果项目中没有 go.mod,需要先运行 go mod init 来初始化模块。然后使用 go get(或其他命令)来引入依赖,下载到本地缓存(~/go/pkg/mod),并更新 go.mod,添加对应的模块路径和版本要求。
  • Makefile:是一个用于自动化构建、测试和其他任务的工具。它允许你定义复杂的构建流程和依赖关系。
    Makefile 可以包含多个targets,例如 build、test、clean 等。make 命令会查找当前目录下的 Makefile 并执行指定的目标。
    构建过程本身不负责引入依赖,它假定所有必要的依赖已经通过 go.mod 文件声明并被解析(否则报错),将编译代码并链接所有必要的依赖来生成最终的可执行文件或库文件。如果 go.mod 中声明的依赖尚未下载,Go 工具链会自动为你下载这些依赖。如果你的项目中没有 go.mod,go build 将无法正确识别和下载依赖(除非是 Go 1.11 前的版本)
  • 项目构建…
    • 对于一个带有 go.mod 文件的 Go 项目,项目所需的依赖不会自动下载到本地环境中。需要手动运行 go mod downloadgo mod tidy 来下载依赖,或者直接运行 go build 以触发依赖的下载。
    • 如果本地已有的依赖版本与 go.mod 文件中声明的版本不同,Go 工具链会优先使用 go.mod 文件中指定的版本。Go 会从远程仓库下载与 go.mod 兼容的依赖版本,并将其存储在本地缓存目录(通常是 ~/go/pkg/mod)中。
    • Go 项目使用的依赖是项目特定的,存储在项目的本地缓存中,而不是全局的 GOPATH 或者 GOROOT 目录。这意味着不同的项目可以依赖同一库的不同版本,而不会发生冲突。其实际工作目录go env中的配置(看上去是如果配置全局 PATH=%GOPATH%\bin 就使用 GOPATH 中的go.exe和pkg)
    • 对于复杂的构建流程和依赖关系,有时还需要根据 Makefile 文件,执行 build来构建项目。
  • VSCode 连接 WSL的Linux环境 开发 Go 项目(先用ubuntu开发,后再centos验证)
    1
    2
    3
    4
    5
    6
    7
    D:\Users\caifeng7>wsl -l -v
    NAME STATE VERSION
    * Ubuntu Running 2
    CentOS7 Running 2

    D:\Users\caifeng7>wsl # 管理员cmd进入wsl的默认linux发行版
    root@caifeng7138:/mnt/d/Users/caifeng7#
    安装go到linux,配置ubuntu上的golang开发环境(centos使用yum管理包)
    1
    2
    sudo apt update
    sudo apt install golang-go
    将GOPATH/bin添加到的PATH环境变量中
    1
    2
    echo 'export PATH=$PATH:/root/go/bin' >> ~/.bashrc
    source ~/.bashrc
    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
    root@caifeng7138:/mnt/c/Windows/system32# go env
    GO111MODULE=''
    GOARCH='amd64'
    GOBIN='' # 编译后的二进制文件的存放位置
    GOCACHE='/root/.cache/go-build' # Go编译缓存的路径
    GOENV='/root/.config/go/env'
    GOEXE=''
    GOEXPERIMENT=''
    GOFLAGS=''
    GOHOSTARCH='amd64'
    GOHOSTOS='linux'
    GOINSECURE=''
    GOMODCACHE='/root/go/pkg/mod'
    GONOPROXY=''
    GONOSUMDB=''
    GOOS='linux'
    GOPATH='/root/go' # Go工作空间的根目录,用于存放你的Go项目和依赖。
    GOPRIVATE=''
    GOPROXY='https://proxy.golang.org,direct' # 修改代理 export GOPROXY=https://goproxy.cn,direct
    GOROOT='/usr/lib/go-1.22' # Go语言的安装目录,包含了Go的编译器、标准库和工具。
    GOSUMDB='sum.golang.org'
    GOTMPDIR=''
    GOTOOLCHAIN='auto'
    GOTOOLDIR='/usr/lib/go-1.22/pkg/tool/linux_amd64'
    GOVCS=''
    GOVERSION='go1.22.2'
    GCCGO='gccgo'
    GOAMD64='v1'
    AR='ar'
    CC='gcc'
    CXX='g++'
    CGO_ENABLED='1'
    GOMOD='/dev/null'
    GOWORK=''
    CGO_CFLAGS='-O2 -g'
    CGO_CPPFLAGS=''
    CGO_CXXFLAGS='-O2 -g'
    CGO_FFLAGS='-O2 -g'
    CGO_LDFLAGS='-O2 -g'
    PKG_CONFIG='pkg-config'
    GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffil...'
    VSCode使用WSL:Remote Explorer选择linux发行版,使用Ubuntu环境打开Go项目目录: /mnt/d/code/datamars-agent/
    此时自动下载了项目go.mod要求的go版本? 项目中的环境变量发生变化 ↓
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    root@caifeng7138:/mnt/d/code/datamars-agent# go env
    # 个人用户的Go工作空间。用于存放Go项目和依赖
    GOPATH='/root/go'
    # Go的安装目录,指向通过模块系统安装的一个特定版本(1.22.4),而不是 /usr/lib/go(全局路径)
    GOROOT='/root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.4.linux-amd64'
    # Go工具链的安装目录
    GOTOOLDIR='/root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.4.linux-amd64/pkg/tool/linux_amd64'
    GOVERSION='go1.22.4'
    # Go模块缓存的路径
    GOMOD='/mnt/d/code/datamars-agent/go.mod'
    在WSL中,安装protoc,添加到PATH;
    执行make,通过makefile中的配置构建项目,编译proto文件,生成Go,grpc程序
    Protobuf 是一种由 Google 开发的用于序列化结构化数据的语言中立、平台中立的可扩展机制,Protoc 可以将 定义了数据结构和序列化规则的 .proto 文件编译生成包括 Go,Java的多种语言。用于grpc请求的数据同步。。

Lic Ai

保 险

社会保障

通过对国民收入的再分配,使公民在遇到特定风险或困难时能够获得基本生活保障。本质是追求一种公平。

社会保险

  1. 养老保险:为了保障老年人退休后的基本生活,通过个人和单位的缴费,在退休后按月领取养老金。个人在达到法定退休年龄时,如果满足缴费年限等条件,就可以领取养老金。
  2. 医疗保险:旨在为参保人员提供医疗费用的报销或补偿,以减轻医疗费用的负担。参保人员在生病住院或接受治疗时,可以通过医保报销一部分或全部的医疗费用。
  3. 失业保险:为因非个人原因失去工作的参保人员提供一定时间内的生活保障金,帮助他们在失业期间维持基本生活。
  4. 工伤保险:用于保障因工作原因遭受意外伤害或职业病的劳动者,提供医疗费用和伤残补助,甚至提供生活保障金等。
  5. 生育保险:主要为参保女性在生育期间提供一定的经济补助和医疗费用报销。

社保卡

佛山社保卡如何申领?“一人一卡”清理又是个啥? https://mp.weixin.qq.com/s/9uQ5qGOYv35BGRgs4OvzvA
没领佛山社保卡可以使用医保报销吗?可以,微信支付时会显示报销数额。
如何使用医保余额?办理实体医保卡,可以查到余额。

医保

  • 医疗保险是一种社会保障制度。通常由个人和单位共同缴纳,参保人员在医疗机构就诊时可以通过医保报销部分或全部的医疗费用。
    通过医保报销的费用是由医保基金支付的,而这个基金是由全体参保人员(包括你和你的单位)共同缴纳的资金组成的。个人缴纳的那部分医保费用只是整个基金的一部分,而报销的金额往往远超过个人单独缴纳的部分,这正是医保体系的优势所在
  • 佛山医疗保险享受指引
    1. 门诊选点:公众号-粤医保
    2. 异地就医备案
      办理条件:异地安置退休人员、异地长期居住人员、常驻异地工作人员、临时外出就医人员
      办理途径:公众号-粤医保-线上办理-异地就医备案
    3. 医保报销方式
      定点联网机构住院(?)费用:凭社保卡/医保电子凭证/身份证现场结算;未能即使结算的,先挂账后结算;无法补记账结算的,垫付费用后申请零星保险。(医保里的钱可以用吗?怎么用?
      零星报销:提供资料到参保所属医保经办机构办理
      https://ihr.midea.com/selfhelp/ihr/selfhelp/pdf?businessId=8a928ead90719d3201907b3b580d00b7&mc_widget_identifier=com.midea.msd.ihrcommonmx&scene=selfhelp_sdc&page=showPdf&type=1

住房公积金

  • 住房公积金是一种社会福利制度,而不是严格意义上的社会保障。住房公积金是国家为帮助职工解决住房问题而设立的一项长期住房储金。它由职工个人和单位共同缴纳,归职工个人所有,并在一定条件下用于购房、建房、房屋维修或者租房等住房相关支出。
  • 职工可以在以下情况下提取住房公积金:购买、建造、翻建、大修自住住房。偿还住房贷款本息(利率低)。支付房租。离职或退休后可以提取全部住房公积金。
  • 住房公积金可以断缴。如果有购房计划或其他需要公积金支持的需求,最好避免长时间断缴。
    • 可以断缴:如果你换工作、辞职、或者暂时没有固定工作,住房公积金可以暂时断缴。没有法律强制要求必须连续缴纳。一旦找到新工作或稳定下来,你可以恢复缴纳住房公积金。新单位会重新为你开设账户或继续使用之前的账户。
    • 断缴的影响
      贷款购房影响影响贷款资格,在申请住房公积金贷款时,很多地方要求申请人在最近的6个月或12个月内连续缴纳公积金。如果公积金断缴,可能会影响你申请住房公积金贷款的资格。贷款额度影响,公积金贷款额度通常与公积金的缴纳时间和余额挂钩。如果断缴时间较长,贷款额度可能会受到影响。
      影响提取条件:如果计划通过提取公积金支付房租或购房时,断缴可能会影响提取公积金的条件,因为很多地方要求提取前必须有一定的连续缴纳记录。
    • 如何减少断缴的影响:1、补缴:在新单位入职后,你可以尝试与单位协商,进行断缴期间的补缴。以恢复在断缴期间的公积金记录。2、灵活就业人员公积金:有些城市允许灵活就业人员或个体工商户自行缴纳公积金,以避免断缴。
  • 住房公积金在中国是可以随着工作地点的变更而进行转移的。通常的步骤是:在新城市的新工作单位会为你开设一个新的住房公积金账户。向新单位的公积金管理中心申请将旧账户的公积金余额转入新账户。新单位所在的公积金管理中心会联系原城市的公积金管理中心,办理资金转移。公积金转移通常需要一定时间,一旦转移完成,你的住房公积金余额将会合并到新城市的账户中。
  • 佛山公积金提取:wx公众号办理租房提取业务,选择按年/月提取,隔日到账。

个人所得税

工资条计算步骤

  1. 应发合计 = 基本工资 + 考评绩效 + 其他发放 + 福利
    2月说明:12330=3555+8445+0+330(餐补)
  2. 实发工资 = 应发合计 - 代扣代缴
    代扣代缴项:包括五险一金(专项扣除)、本月预扣个人所得税。
    2月说明::9612.2=12330-2717.8
  3. 累计预扣预缴应纳税所得额 = 累计收入 - 累计减除费用 - 累计专项扣除 - 累计专项附加扣除
    累计减除费用:每月5,000元起征点,年度累计为60,000元(如工作满12个月)。
    专项附加扣除:包括子女教育(每月2,000元/孩)、房贷利息(每月1,000元)、赡养老人(每月2,000元)等,需在个税APP填报。
    2月说明:11101.74=35710(2024.12起重新计算?) - 15000 - 6308.26(累计个人缴纳社保&公积金) - 3300, 公式逻辑:逐月累计收入及扣除项,动态计算全年税基。
  4. 扣税 = 累计预扣预缴应纳税所得额 × 税率 - 速算扣除数 - 累计已预扣预缴税额
    税率表:采用七级超额累进税率(3%-45%),按月累计收入匹配税率档位。若某月税额突增,可能是累计收入进入更高税率区间,属正常现象。
    示例:若累计应纳税所得额36,000元,对应税率10%,速算扣除数2,520元,则当月预扣税额 = 36,000×10% - 2,520 - 已缴税额。

退税条件与计算

个人所得税丨2024年个人综合所得年度汇算操作指引 https://mapmpm5.midea.com/newservicenopages/#/content/preview?id=1896853229331374081
个人的汇算清缴,就是把您2024年一整年取得的劳动收入(税法中叫“综合所得”,具体指工资薪金、劳务报酬、稿酬、特许权使用费共4项)合并计算个税,得出应纳个人所得税,与2024年实际预扣预缴的个税比较,税款多退少补。预缴税款与实际应缴税款产生差异的核心原因在于预扣预缴机制与全年综合计算的逻辑差异
预扣预缴是基于局部数据和固定规则的估算,而汇算清缴是基于完整数据和真实扣除的精准计算
建议:通过“个人所得税APP”提前核对收入明细和扣除项,利用汇算规则合法降低税负(如优化年终奖计税方式)。以下是具体原因分析:

  1. 收入结构与预扣方式的差异
    预扣预缴的“分段计算”特性 : 工资薪金采用累计预扣法,按月计算税款(逐月累加收入并匹配税率),而劳务报酬、稿酬等按固定比例预扣(如劳务报酬预扣率20%)。若全年收入波动大(如年终奖集中发放)或存在多处收入,可能导致:1. 前期预缴税率低,后期税率跳档:例如前半年收入低按3%预扣,后半年累计收入超过36,000元后按10%计税,全年汇算需补差额。 2. 劳务报酬预扣率高于综合税率:劳务报酬预扣时按20%扣税,但全年综合所得实际税率可能仅为3%或10%,汇算时可退税。
    年终奖计税方式选择的影响 : 年终奖可选择单独计税(按“年终奖/12”匹配月度税率)或合并计税(并入综合所得)。若预缴时选择单独计税,但汇算时合并后适用更高税率,则需补税。
  2. 扣除项目的动态调整
    专项附加扣除的补充申报 :预缴时未填报的专项附加扣除(如租房、赡养老人等),可在汇算时补充扣除,直接减少应纳税额。例如:租房扣除(1,500元/月):若全年未申报,汇算时补充可减少18,000元应税所得额,按10%税率计算可退1,800元。大病医疗扣除:只能在次年汇算时申报,直接影响退税额度。
    • 其他扣除项的汇算调整:如公益捐赠(需在汇算时提供凭证)、职业资格继续教育等扣除,预缴时未计入的,汇算时可补充抵扣。
  3. 收入合并的税负效应
    综合所得合并计算的税率跃升 :工资、劳务报酬等四项收入合并后,可能使全年应纳税所得额跨越税率档位。例如:单独计算时未超档:工资10万元(适用10%税率)+ 劳务报酬5万元(预扣20%但实际税率10%),合并后15万元应纳税所得额适用20%税率,需补税。多处收入叠加:从两家单位取得工资,每家均按5,000元起征点预扣,但全年合并收入超过6万元的部分需补税。
    • 稿酬的特殊计算规则:按“(稿酬收入×80%)×70%”计入综合所得,若预缴时未按此规则计算(如企业误操作)汇算时需调整。
  4. 政策与实操的特殊情形
    免税收入的误计入 : 部分免税补贴(如差旅津贴、误餐补助)若被错误计入工资,预缴时多缴税款,汇算时可申请剔除并退税。
    预缴申报的误差 : 企业可能因操作失误导致预缴税款错误(如漏报专项扣除),需通过汇算修正。

劳动合同

  1. N+1补偿的适用场景与条件
    触发条件企业合法解除劳动合同但未提前30日通知,需支付“N+1”(经济补偿金+代通知金)。
    • 具体情形包括:
    ◦ 劳动者医疗期满后无法返岗且无法调岗;
    ◦ 劳动者经培训或调岗仍不胜任工作;
    ◦ 客观情况重大变化(如部门撤销、搬迁)导致合同无法履行。
  2. 计算规则
    N:按工作年限计算(每满1年支付1个月工资,不满半年按0.5个月计算)。 N按离职前12个月平均工资计算,包含奖金、津贴等。
    +1:额外支付1个月工资(按解除合同前12个月平均工资或上月工资计算)。
    • ​月工资上限:若月工资高于当地上年度职工月平均工资3倍,按3倍标准支付且年限最高12年
  3. 员工主动离职的经济补偿可能性
    一般情况:员工主动离职无补偿。
    例外情形(企业存在过错,员工被迫离职):
    • 企业未足额支付工资、未缴社保、未提供劳动保护等(依据《劳动合同法》第38条);
    • 此时员工可主张N倍经济补偿金(无“+1”),但需书面通知并保留证据(如工资流水、社保记录)。
  4. 违法解除劳动合同的赔偿标准(2N) :若企业无合法依据解除合同(如无故辞退、程序违法、裁撤孕期员工等),需支付2N赔偿金(经济补偿金的双倍)。
  5. 实务对比与维权建议
    情形 补偿标准 法律依据 维权路径
    企业合法解除未提前通知 N+1 《劳动合同法》第40条 协商或仲裁主张代通知金
    企业违法解除 2N 《劳动合同法》第87条 劳动仲裁或诉讼索赔双倍赔偿
    员工被迫离职(企业过错) N 《劳动合同法》第38条 提交证据申请仲裁

商业保险

【当代年轻人没了铁饭碗,还能负重奔跑多久?北京二胎家庭如何转移风险?】 https://www.bilibili.com/video/BV16F4m1V7wv/?share_source=copy_web&vd_source=2cbe8cdd54a75d0c43fcdefa624d3fbe

“年纪轻的一定要买百万医疗险和意外险,还有抗癌险,关键时候真的能救命的,有余裕买个重疾险,如果生病了确证了就能拿到几十万的保额,可以作为治疗和康复期的生活费开销,医疗费有医保和百万医疗险,到时候真生比较严重的病百分之九十能报销,年纪轻买了便宜加起来一年千把块,父母没生病可以给他们买众民保和中银全名保,还有当地的惠民保三个加起来六百块不到,不看健康告知年龄大可以报销,配合医保大概能报销八十花的多甚至能报销到九十,给家里老人多套保障,不会出现得了病只能放弃的人间惨剧,如果老人没医保可以买城乡医保一年几百块”

mom:东莞农村?居民社保医保,莞家福,莞家福居民保?,midea家属保险

2025年度商业保险开始投保了~ 员工(统一投保无需美福下单)本人及任选两名家属的免费/自付费升级方案 https://mapnew5.midea.com/newservicenopages/#/content/detail?id=1902351310562877442&type=2

快速了解一个保险的7个核心要素

  1. 保障范围(核心)
    • 保什么:疾病类型(如60种重疾)、门诊/住院/手术等报销范围。
    • 特别关注:是否包含社保外费用(如进口药、质子重离子)、癌症特药清单。
    • 图片信息提示:图中“重大疾病列表”需核对乳腺癌是否被列为赔付疾病。
  2. 购买条件
    • 年龄:是否限制60岁以上?续保是否允许超龄?
    • 健康告知:是否询问癌症病史?如有,如何核保?(图片中未提及健康告知,可能宽松)
    • 等待期:生效前等待期(通常30-90天,重疾险可能180天)。
  3. 免赔额与报销比例
    免赔额:如“意外住院补贴限额100元/天”可能指单日免赔额。
    报销比例:社保内是否100%?社保外是否有比例限制?(图中“社保范围内7.5万/位”需进一步解读)。
  4. 既往症条款
    • 关键问题:乳腺癌术后治疗是否被定义为“既往症”?若属于,报销比例是多少?(图中“免检责任”可能指部分疾病免检,需核实)
  5. 保额与限额
    • 单项限额:如“意外住院补贴7.5万/位”是否够用?
    • 年度总限额:重大疾病总保额是否充足(至少100万以上)。
  6. 增值服务
    • 实用性:医疗垫付、重疾绿通、二次诊疗意见(图中未提及,需向HR确认)。
  7. 免责条款
    • 雷区排查:如“不覆盖胎液”(图片底部注释)可能指免责先天性疾病,需明确免责清单。

从报销顺序理解

  1. “社保医保”“惠民保”:基本保障,看病报销“社保范围”内的如60%
  2. 团保商业保险:企业与保险公司合作提供,一方面能够报销“社保范围”内剩余40%中的部分,一方面能保重疾住院等
  3. 百万医疗险:看病报销“社保范围”外的部分,?有患癌史无法购买,可选择众民保
  4. 重疾保:以上“医疗险”报销看病本身的费用,而“重疾险”为支付单笔保费,用于后续正常生活;需要安全告知
  5. 意外险:负责意外事故以及某些“医疗险”不涵盖的病
  6. 可以按照“医疗险”,“重疾险”,“意外险”来搭配购买,但也存在商业保险覆盖其中多种的能力

根据您母亲的情况(47岁、乳腺癌病史、已参保东莞社保及莞家福),结合健康限制和保障需求,以下是商业保险的配置建议及具体产品的分析:

一、基础保障优先级(必选)

  1. 普惠型医疗险(补充社保和莞家福)
    推荐产品:全国版惠民保(如众民保)、东莞本地惠民保(如已参保莞家福需确认既往症规则)
    适用性
    ◦ 无健康告知,覆盖乳腺癌术后治疗费用(报销比例通常为30%-50%,需查看具体条款)。
    ◦ 年保费约100-200元,保额高达300万,覆盖医保内外费用(部分产品含特药)。
    注意事项
    ◦ 优先选择支持“乳腺癌复发治疗”的产品,如众民保对复发医疗费按比例报销。
  2. 乳腺癌复发险(专项核心保障)
    推荐产品
    泰康粉红卫士:0-2期患者可投保,复发或转移一次性赔付最高50万,报销型最高100万(含41种特药)。
    太平洋粉红守护:支持0-2期患者,可选对侧乳腺癌保障,含48种特药报销,续保稳定性较好。
    适用性
    ◦ 覆盖乳腺癌复发、转移、对侧新发,报销比例100%(含社保外费用)。
    ◦ 年保费约1400-3000元(根据保额选择),等待期90天。

二、补充保障(根据预算可选)

  1. 意外险
    推荐产品:众安高危职业意外险、平安综合意外险
    适用性
    ◦ 无健康告知,保意外身故/伤残(50-100万)、意外医疗(不限社保)。
    ◦ 年保费约200-500元,杠杆率高。
  2. 团体医疗险(企业合作产品)
    适用性
    ◦ 您提到的太平洋人寿企业合作套餐,若无健康告知或宽松告知,可作为基础住院医疗补充(覆盖门诊住院费用)。
    ◦ 需确认是否覆盖社保外费用及乳腺癌复发相关治疗。

三、不推荐或限制类产品

  1. 常规重疾险/百万医疗险
    • 因乳腺癌病史,99%产品拒保或除外责任。
  2. 防癌医疗险
    • 仅少数产品允许投保(如支付宝终身防癌险),但通常除外乳腺癌责任。

具体产品对比分析

产品类型 推荐产品 核心优势 局限性 年保费参考
乳腺癌复发险 泰康粉红卫士 保费最低(1400元起),特药覆盖最全(41种) 仅限0-2期患者,续保需审核 1400-5000元
乳腺癌复发险 太平洋粉红守护 含对侧乳腺癌保障,特药清单更新快(48种) 仅限0-2期患者,保费较高 2000-8000元
惠民保 众民保(全国版) 覆盖复发治疗,含CAR-T疗法和质子重离子 免赔额高(2万),报销比例50%-80% 150-300元
团体医疗险 太平洋企业合作套餐 无健康告知,可能覆盖既往症 需确认保额和报销范围(可能仅限社保内) 企业合作价(待确认)

四、配置方案建议

  1. 预算有限方案(年保费约1600元)
    必选:泰康粉红卫士(基础型1400元)+ 众民保(200元)。
    作用:覆盖乳腺癌复发高额医疗费,补充普惠报销。
  2. 全面保障方案(年保费约5000元)
    必选:太平洋粉红守护(含特药责任3000元)+ 众民保(200元)+ 团体医疗险(企业合作)。
    可选:叠加意外险(300元),覆盖意外风险。
  3. 区分保险类型,明确赔付规则
  • 可叠加赔付的保险类型(给付型)
    重疾险:若确诊合同约定的疾病(如癌症、心梗等),不同保司的重疾险可同时赔付,互不影响。例如:投保两份50万保额的重疾险,确诊后可获100万赔付。
    寿险/意外险身故/伤残保障:身故或伤残保额可叠加赔付,但未成年人受保额限制(如10岁以下身故保额最高20万)。
    年金险:按合同约定固定给付生存金,多份保单可同时领取。
  • 不可叠加赔付的保险类型(报销补偿型)
    医疗险(含意外医疗):以实际医疗费用为上限,发票原件仅能用于一次报销。若A公司已全额赔付,B公司不再赔付;若A公司仅报销80%,剩余20%可向B公司申请(需提供分割单)。
    财产险(车险、家财险):遵循损失补偿原则,赔付总和不超过实际损失。例如:车辆全损价值50万,即使投保两份车险,最高仅赔50万。
  1. 保障范围叠加策略
  • 优先覆盖不同风险场景
    疾病+意外+身故组合:例如「重疾险+医疗险+意外险」,覆盖疾病治疗、意外伤害和身故风险。
    高低保额搭配:小额医疗险(覆盖1万内费用) + 百万医疗险(覆盖大额住院费用),减少免赔额影响。
  • 避免重复投保同类报销型保险
    • 同时购买多份百万医疗险无意义(均需扣除1万免赔额),但可搭配无免赔额的小额医疗险。
  • 注意隐性条款冲突
    • 部分医疗险限制“仅报销社保目录内费用”,若另一份医疗险覆盖社保外费用,可互补。
  1. 总结建议
    合理搭配类型:给付型(重疾、寿险) + 报销型(医疗险) + 财产险,避免同类报销险重复。
    重点核查条款:健康告知、免责范围、赔付优先级(如医疗险报销顺序)。
    动态调整保障:定期检视保单,根据家庭阶段(如生子、购房)增减保额或险种。

五、注意事项

  1. 健康告知:购买前务必确认条款中的“既往症定义”,乳腺癌相关治疗可能被除外。
  2. 报销材料:需保存医疗发票、诊断证明、用药清单等,多数产品需先经医保报销。
  3. 续保稳定性:众民保为1年期产品,不保证续保,但众安产品历史稳定性较好。

建议优先投保乳腺癌复发险(专项保障),再补充惠民保和意外险,最后根据企业团体险条款决定是否叠加。
建议通过保险顾问协助核保(如众安、慧择平台),确保理赔无争议。


投资

三、个性化投资策略建议

1. 资金配置方案(月均5000元)

类别 比例 工具示例 目的
学习投入 10% 付费研报、专业课程 知识体系构建
模拟交易 0% 同花顺模拟盘、Backtrader 策略验证
指数定投 40% 沪深300ETF(A股) 强制储蓄+市场β收益
行业ETF 30% 半导体ETF(512480) 把握结构性机会
现金储备 20% 货币基金 应对黑天鹅事件

2. 技术赋能投资的具体路径

  • 量化入门
    1. 用Python复现经典策略(如双均线策略)
    2. 开发舆情监控爬虫(抓取雪球、股吧热词)
    3. 构建多因子选股模型(参考WorldQuant Alpha库)
  • 工具推荐
    • 数据平台:AKShare(开源替代Tushare)
    • 回测框架:QLib(阿里开源)
    • 可视化:Plotly+Dash构建交互式看板

3. 阶段目标管理

  • 新手期(0-1年)
    • 目标:年化收益率跑赢货币基金(2%-3%)
    • 关键动作:完成10个策略回测、建立投资checklist
  • 进阶期(1-3年)
    • 目标:年化收益率8%-10%(同期沪深300水平)
    • 关键动作:开发自动化交易信号系统

四、风险控制工具箱

  1. 极端情况应对预案
    • 设置动态止损线(如回撤超15%强制减仓)
    • 保留3-6个月生活费的现金储备
    • 建立黑天鹅事件清单(如中美脱钩、疫情反复)
  2. 认知偏差矫正方法
    • 交易日志模板:
      1
      2
      3
      | 日期 | 操作 | 决策依据 | 情绪状态 | 事后评估 |
      |------|------|----------|----------|----------|
      | 2023-08-01 | 买入XX股 | MACD金叉 | 焦虑追涨 | 错误:未考虑成交量萎缩 |
  3. 推荐监控指标
    • 十年期美债收益率(全球资产定价锚)
    • 人民币汇率波动区间
    • 融资融券余额变化率

六、推荐资源清单

  1. 数据源
    • 国家统计局宏观数据库
    • FRED(美联储经济数据)
  2. 工具链
    • Jupyter Notebook + Pandas(数据分析)
    • TensorFlow/PyTorch(预测模型)
  3. 深度阅读
    • 《主动型指数投资》(王延巍)
    • 《量化投资:以Python为工具》(蔡立耑)

下一步行动建议

  1. 本周内注册模拟交易账户
  2. 用Matplotlib复现近5年行业轮动图谱
  3. 加入量化投资开源社区(如JoinQuant)

你的技术背景是独特优势,建议从**”工程师型投资者”路径切入,将编程能力转化为投资生产力。记住:在认知不充分时,不亏损追求收益**更重要。

美 的 Midea

24.07.08

入职第一天,熟悉运维平台,目标实现其自动化,后续参与到美的云,?
熟悉新旧平台的功能和调用关系,拆分业务需求开发步骤,编写文档,每日汇报进展,熟悉开发流程、、
软工院作为非互联网公司的非核心业务的底层平台建设部门,结果导向,组员多一年社招;)无校招培养。?

7.17 - 7.29

EDP培训 + MGC(头脑风暴/产品调研/拉通对齐>>技术)
T型人才(广度+深度),开发技术+产品思维->架构师
不设限,主动承担任务,机会莫名来:) take other people’s jobs and become indispensable to the team..

复杂的事情简单化(思考简化),简单的事情复杂化(做到极致)
工作就是生活,生活就是工作,不需要平衡(找到热爱的工作)
成功的百分比 = 做事 / (个人 + 做事);做事的比例越大,成功的概率越大
alex . scope of teamwork minus everyone's responsibility is not null

Allen: 向上管理?× 向上反馈,同步进展
MGC结营
融入团队?主动承担?谈论未知?如何选择自己在团队中的角色,人设??
圈子

8.17

佛山校友会迎新
“努力会发光,先有为后有位”,“头三年不要动,把这一套学会”
程序员的本质核心竞争力是什么?1.开发都是那一套 2.专精一个领域 3.meet新公司的需求 4.解决问题的能力

8.19

ALEX:最近和一些软工院同学深入聊了一下他们的职业发展规划,

8.22 需求一

华为云主机开/关机/重启自动化 : mq、定时任务、公有云api、crud、、
完成第一版8.14,自测8.15,,merge request,code review,sit,测试,uat,发版8.22、、
反思、、在开发同事的指导下完成了开发,不具备独立调研和开发能力,,
缺少对产品的思考??没有对需求进行120%的思考和完成。。

8.24

顺德校友会迎新
why Midea?1.生活成本低(特别是住宿好通勤方便)2.相比下工作轻松(能够有自己的时间学习业务以外的东西)
思考自己在..年后会到什么层次(本科毕业+6y ?= 博士毕业起步)。。阶段性目标

8.27 Steven:

深入一个领域,,
先做一点功能点,然后负责一个模块,到不同系统的交互、、
多学基础,与外包的区别。。与人沟通的能力
幂等,整体设计,微服务治理,看项目源码,,
干半年就不是应届生了。社会很残酷,前两年要快速成长;思考两/五年后的情况、、
开发整个过一遍,打包,发版,,
多讨论,多问,code review
!!邮件:设计一个功能,,关注点,逻辑路径,通用性,,如何表述。。?! –>

Linux基本命令:
netstat ; top; awk ; dstat; iostat; lsof; free;uptime;dmesg ;dig ; nslookup

vim/vi的基本快捷命令(shift + g; dd;yy等)

终端的一些快捷命令(ctrl + a; ctrl + e)

9.9 并发先查后改

方法:事务+行锁【悲观锁】,避免在高并发场景下先读后写导致多个线程同时读取相同的值然后同时写入引发数据不一致的问题
测试:线程池多线程访问,打印数据,排查重复值;考虑数据库连接池配置
思考:项目部署到多节点下,则是多进程的多线程环境,需要用Redis分布式锁,或者唯一的全局数据库节点加锁;
单节点的多线程才能用synchronized?、

  • 优化:事实上,update方法会加行锁,所以直接先update后select即可,无需select…for update
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Service
    public class NumberService {

    @Autowired
    private NumberMapper numberMapper;

    @Transactional // 在业务逻辑层开启事务,确保整个操作是原子的
    public void incrementNumber(Long id) {
    // 查询并锁定行
    NumberEntity numberEntity = numberMapper.selectForUpdate(id);
    // 读取后进行计算并更新,保证只有当前事务可以操作该行
    int currentNumber = numberEntity.getNumber();
    numberEntity.setNumber(currentNumber + 1);
    // 更新数据库
    numberMapper.updateById(numberEntity);
    // 更新完成后提交事务,释放锁
    }
    }
    1
    2
    3
    4
    5
    6
    7
    @Mapper
    public interface NumberMapper extends BaseMapper<NumberEntity> {

    // 使用 FOR UPDATE 读取并锁定行,防止其他事务并发读取该行
    @Select("SELECT * FROM number_table WHERE id = #{id} FOR UPDATE")
    NumberEntity selectForUpdate(Long id);
    }

9.12 需求二

Azure公有云主机申请 :根据云管界面配置配齐参数发送报文到作业平台,完成自动化主机创建和标准化
对其参数,连续加班,9.9完成第一版,9.10上sit前端联调,9.11开发部分发邮件,9.12上uat,6.同步DDL&DML,7.发版,验收成功
接触运维协同,,code review,联调,,集成,部署,流水线,,

9.14

窝囊费:)120

9.26 需求三:

Azure公有云主机回收/开/关机/重启 : 调研AzureApi和测试方法,开发,,
9.23回收上sit,9.24开关机重启代码重构(原华为云方法过于通用),9.25bug毁了我的足球梦,9.26配置ngix上uat,验收
思考:自测可以 1.全流程验证 2.单独功能验证 3.考虑开发与测试环境的区别(ping的包装方法/命令行执行在开发/测试环境的区别)
后续:完善公有云开发(Azure回收配额,ip,失败邮件),后续由运维平台MOPS -> 参与到数据库开发

10.12 需求四:

任务触发式失败邮件完成,改造为工单定时任务扫描式,10.17上线
邮件通用性??工单+定时任务层面的通用,,

10.14

数据管控平台DataMars: 云管cmcloud开发功能,先提供内部服务,后到SAAS,,
InfluxDB备份恢复 1 调研 2 手工实现 3 详细文档
2-3月时间,不要求11月上线,整体设计,转正答辩
api,数据库内核?,容器,k8s
容器,登录主机,查看docker实例,操作数据库实例

10.22 K8S验证InfluxDB Cluster实例导入导出

  1. FinalShell客户端:主要用于服务器管理和运维。支持 SSH、SFTP 等多种协议,方便用户通过图形界面进行远程连接和操作。
  2. 跳板机/堡垒机: SSH连接,登录堡垒机opsec.midea.com,mip账密 + OTP验证
  3. 资产列表中选择指定环境下的主机,InfluxDB多节点部署在对应环境的几台主机上
  4. 切换用户:rouser,apps,root
    1
    rouser20@(datamars)mhpl74337-10.20.248.65 ~$ sudo su - apps
  5. K8S入门 https://zhuanlan.zhihu.com/p/32618563
    • Namespace(命名空间,是一个逻辑隔离的环境,用于资源分组和隔离) -> InfluxDB集群(可以有多个),Pod(Kubernetes的基本计算单元) -> InfluxDB集群节点(逻辑上),容器 -> InfluxDB单例(物理上)
    • Namespace:作为最顶层的资源,实现了资源的逻辑隔离。
    • StatefulSet:对于有状态的服务如数据库,K8s 推荐使用 StatefulSet 进行管理,确保每个 Pod 都有一个持久的唯一标识并提供稳定的网络标识和存储。 InfluxDB 集群中,StatefulSet 用于管理数据节点和元节点。
    • Pod 是最基本的部署单元,它是可以被创建和管理的最小部署对象。当创建一个 InfluxDB 集群实例时,StatefulSet 会用来管理 Pod 的生命周期(而不是直接创建Pod)。
    • InfluxDB 集群部署:对于一个 Namespace下的几个 Pod,这些节点会通过 StatefulSet或者 Deployment来进行管理和部署。在实际操作中,创建 InfluxDB 集群实例的 Helm Chart 或者 Operator 通常会自动化这些资源的创建过程。
    • 通信调度:K8s 中,Pod 之间的通信通常通过 Service 来进行。Service 会为一组 Pod 提供一个稳定的 IP 地址和 DNS 名称。对于 InfluxDB 集群,可能会有一个或多个 Service 来管理数据节点和元数据节点之间的通信。
    • 查看实例root密码
      kubectl get secrets -n influxdb
      kubectl get secrets -n influxdb influxdb-e73f149ff7192bd87d190-influxdb -o yaml
  6. 连接主机mhpl74337(datamars-uat的三台服务器之一),查看 influxdb 命名空间下的所有 Stateful
    1
    2
    3
    4
    5
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get sts -n influxdb
    NAME READY AGE
    influxdb-e2cb6c913a191e56c134e-data 2/2 47d
    influxdb-e2cb6c913a191e56c134e-meta 3/3 47d
    influxdb-e73f149ff7192bd87d190...
    查看 influxdb 命名空间下的所有 Pod 的状态和节点信息
    1
    2
    3
    4
    5
    6
    7
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get pod -n influxdb -o wide
    influxdb-e2cb6c913a191e56c134e-data-0 1/1 Running 0 20d 10.20.205.88 mhpl74338 <none> <none>
    influxdb-e2cb6c913a191e56c134e-data-1 1/1 Running 52 (44h ago) 20d 10.20.206.129 mhpl74344 <none> <none>
    influxdb-e2cb6c913a191e56c134e-meta-0 1/1 Running 28 (11d ago) 20d 10.20.205.170 mhpl74337 <none> <none>
    influxdb-e2cb6c913a191e56c134e-meta-1 1/1 Running 53 (44h ago) 20d 10.20.205.238 mhpl74344 <none> <none>
    influxdb-e2cb6c913a191e56c134e-meta-2 1/1 Running 27 (11d ago) 20d 10.20.206.160 mhpl74337 <none> <none>
    influxdb-e73f149ff7192bd87d190...
    可以看到,在 Kubernetes上创建了2个 InfluxDB集群实例,它们共用一个 Namespace:influxdb,使用 StatefulSet 来创建和管理 Pod。这些 Pod 负责运行 InfluxDB 服务,并由 StatefulSet 确保它们的高可用性和数据持久化。
    对于每个集群实例,有 2个 sts为 meta和 data,分别有2和3个复制,即2个元节点和3个数据节点 Pod,部署在3台服务器上;pod内部共用数据卷,pod之间数据不互通,部署在同一主机上的pod之间可以通过本地机器为中介复制文件。
  7. 查看 influxdb 命名空间下的 service 信息。
    1
    2
    3
    4
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get svc -n influxdb
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    influxdb-e2cb6c913a191e56c134e-data ClusterIP None <none> 8086/TCP,8088/TCP,2003/TCP,4242/TCP,25826/UDP,8089/UDP 39d
    influxdb-e2cb6c913a191e56c134e-meta ClusterIP None <none> 8089/TCP,8091/TCP 39d
    有 2 个Service,用来定义一组Pod的访问策略的抽象。它提供了一种方式,使得外部客户端可以通过一个固定的IP地址和端口访问这些Pod,而不需要关心Pod的实际IP地址和端口。Service会通过选择器(selector)将这些端口映射到后端的Pod上。
  8. kubectl exec 进入指定的 Pod(默认进入其中的第一个容器),并启动一个 bash shell;可以看到当前 InfluxDB 版本是v1.8.10-c1.1.2
    1
    2
    3
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl exec -it influxdb-e2cb6c913a191e56c134e-data-0 -n influxdb -- bash
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influxd version
    InfluxDB v1.8.10-c1.1.2 (git: master 529251fda5d776cf47bb0c247cf81075f2980fed, build: go1.16.15 linux/amd64)
    在使用InfluxDB进行备份和恢复操作时,通常需要在数据节点上执行相关命令(元节点上都没有influx指令。。)
  9. 进入influx命令行界面,验证身份信息;事实上,进入到influxdb实例中,便无需考虑节点,部署,,,了,直接操作数据库和数据
    1
    2
    3
    4
    5
    6
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx
    Connected to http://localhost:8086 version 1.8.10-c1.1.2
    InfluxDB shell version: 1.8.10-c1.1.2
    > auth
    username:
    password:
  10. 数据库导出:容器层面命令,指定 数据文件和 写前日志(WAL)文件的存储目录,将指定数据库中指定时间的数据导出到指定文件。
    1
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_test01_dump_out" -database "test01" -start "2024-10-22T00:00:00Z"
    数据文件复制:1、从pod1复制到本地机器 2、从本地机器复制到部署在同一服务器上的pod2(NODE: mhpl74344)
    1
    2
    sudo kubectl cp influxdb/influxdb-e2cb6c913a191e56c134e-data-1:/influxdb_test01_dump_out data/influxdb_test01_dump_out
    sudo kubectl cp data/influxdb_test01_dump_out influxdb/influxdb-e73f149ff7192bd87d190-data-1:/influxdb_test01_dump_out
    获取密码:查看对应实例的admin账密,解密data
    1
    2
    kubectl get secrets -n influxdb # 看命名空间
    kubectl get secrets -n influxdb influxdb-e73f149ff7192bd87d190-influxdb -o yaml # 看选定实例
    数据库导入:容器层面执行命令,使用admin账号,指定文件、数据库、时间戳精度
    1
    root@influxdb-e73f149ff7192bd87d190-data-1:/# influx -import -path='influxdb_test01_dump_out' -precision=ns -username='admin' -password=''
    实例导出:不加 -database,把influxdb集群实例中所有数据库的数据导出,加 -compress 导出压缩文件
    实例导入:加-compressed 导入压缩文件,本质上是先解压后倒入

11.4 Pod添加datamars-agent Container

  1. 从已有的MongoDB实例中的statefulset配置文件中,找到datamars-agent容器修改配置
    1
    2
    3
    4
    # 导出配置文件
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get sts influxdb-xxxx-xxx -n influxdb -o yaml > influxdb-sts.yaml
    # 文件下载本地
    apps@(datamars)mhpl74337-10.20.248.65 ~$ sz influxdb-sts.yaml
  2. 本地编辑器打开(for convenience)
    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
    spec:
    template:
    spec:
    containers:
    - command: # 找到datamars-agent容器
    # 配置
    image:
    name:
    volumeMounts: # volumeMounts 是在容器层面配置的,定义了容器内的挂载点
    - mountPath: /etc/localtime
    name: host-local-time
    - mountPath: /etc/mongodb-config
    name: config
    - mountPath: /log # /log 挂载了名为 log 的卷。
    name: log
    volumes: # volumes 是在 Pod 层面配置的,定义了 Pod 中可以使用的卷
    - configMap:
    defaultMode: 420
    name: influxdb-e73f149ff7192bd87d190-data
    name: config # config 卷是一个 configMap,实际路径在物理机上并不固定,由 Kubernetes 动态管理
    - hostPath:
    path: /etc/localtime
    type: File
    name: host-local-time # host-local-time 卷是一个 hostPath,路径为 /etc/localtime,类型为 File
    volumeClaimTemplates: # volumeClaimTemplates 是在 StatefulSet 层面配置的,定义了持久化存储卷的声明
    - apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    annotations:
    ext.datamars.org/blkio.throttle.read_iops_device: "8000"
    ext.datamars.org/blkio.throttle.write_iops_device: "8000"
    creationTimestamp: null
    name: log # log 卷声明了一个 PersistentVolumeClaim,请求 50Gi 的存储,实际路径在物理机上由存储类 datamars-default-lvm 管理,具体路径取决于存储类的实现
    namespace: mongodb
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 50Gi
    storageClassName: datamars-default-lvm
    volumeMode: Filesystem
    status:
    phase: Pending
    查看pv(集群层面的存储),pvc(pv的使用规则)
    1
    2
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get pv -n influxdb
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl get pvc -n influxdb
  3. 添加到influxdb-data Statefulset的配置中,手动更新sts配置(导出influxdb-sts.yaml,sz到本地,修改后rz传到服务器)
    1
    kubectl apply -f influxdb-sts.yaml
    或者,直接修改influxdb-data Statefulset的配置中,:wq 保存,成功后自动更新到Pod
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl edit sts -n influxdb influxdb-e73f149ff7192bd87d190-data
    statefulset.apps/influxdb-e73f149ff7192bd87d190-data edited
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl describe sts -n influxdb influxdb-e73f149ff7192bd87d190-data
    # 最下面可以看到事件信息
    Events:
    Type Reason Age From Message
    ---- ------ ---- ---- -------
    Warning FailedCreate 50m statefulset-controller create Pod influxdb-e73f149ff7192bd87d190-data-0 in StatefulSet influxdb-e73f149ff7192bd87d190-data failed error: Pod "influxdb-e73f149ff7192bd87d190-data-0" is invalid: spec.containers[1].volumeMounts[0].name: Not found: "conf"
    Normal SuccessfulCreate 48m (x2 over 12d) statefulset-controller create Pod influxdb-e73f149ff7192bd87d190-data-0 in StatefulSet influxdb-e73f149ff7192bd87d190-data successful
    # 或者看日志
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl logs influxdb-e73f149ff7192bd87d190-data-0 -n influxdb -c datamars-agent
  4. 注意以上操作只是手动修改一个Pod,要想新建Pod中的配置更新,需要通过helm chart配置__

11.11 InfluxDB服务化 :实例备份(调研&开发)

10.14-10.18:看文档,建立InfluxDB集群概念(前期已经也在看了..),建立整体框架概念(apiserver–bakserver–agent)
10.21-10.25:本地容器搭建influxdb集群×,连接服务器测试实例验证功能,了解K8S概念,手动验证实例(库级)导入导出即逻辑备份
10.28-11.01:对其需求(能做但没用户??),完成技术文档框架,开始将功能接入datamars-bakserver,了解golang开发
11.04-11.08:搭建go开发和agent项目环境,,无法理解go项目结构,尝试从bakserver侧理解task下发-接收-执行全流程
11.11:理解所有业务代码(after2weeks)发现task下发无需改动,只需适配influxdb(修改配置类和表),接着打包至sit环境打印log调试
11.12-11.15:研究pod添加container(改sts后delete pod重建),go项目构建(windows尝试配齐开发工具但有些包依赖linux环境)》。
11.18-11.27:本机wsl的ubuntu成功构建起datamars-agent,边抄边做,不用理解其框架?.. 及时请教专家
11.28-12.03:kun哥手改agent代码:)打包image push到dockerhub,宿主机拉取镜像后本地grpcurl调试 pod ip:port 验证功能
12.04-12.09:bakserver打日志流水线部署到uat(集群实例所在环境),通过apiserver-bakserver-agent中的日志验证全流程功能
12.10:验证通过
技术文档先行,将需求拆分成一步步,重要的是要有产出,,能汇报进度。。buffer。。好心态😇不怕叼
关注重要的事情(功能接入已有框架/理解业务逻辑×,功能验证和对齐需求√,开发卡点及时请教)
1/2时间幻想(串联已知信息且验证,对齐上下游并重复验证,本质是开发环境,业务不熟悉),1/4等回复(线上下请教+准备),1/4开发(快乐短暂)
与人沟通是重要的能力。。拉群问。。软件开发还是很残酷的。。

2025.04:重新考虑… 1 influx_inspect export只是把该pod指定数据库(或所有库)的指定时间段的分片数据(tsm&wal)导出line protocol文件,各Data-Pod数据不一致时不能代表整个实例;2 influx -import本质是将数据重新写入,前提要先恢复好shard元数据,,

12.12 年终述职

  • 大家好,那我现在开始~
    七月份入职后,我先是接触了MOPS自动化运维的开发任务:)
  • 总的来说,这五个月以来,我作为团队和开发领域的新人,得到了循序渐进的挑战和成长。了解到如何作为团队中的一个成员进行开发,能够理清一些比较复杂的业务逻辑,能够尝试进行调研和方案设计 ~
    相比于赋能团队,我觉得更多的是还我自身需要补齐能力;也许目前开发效率低,本质是对业务的不熟悉和开发技能的缺失,但是除了我觉得提升开发能力,还应该具备产品思考的能力,服务客户。我的汇报就到这里。

12.27 成长对话

  1. 本年度关键战役及成果产出
  2. 面对未来1-2年的职业规划
    作为团队和开发新人,在这个阶段希望能锻炼自己的专业能力,补齐开发所需地各项能力,积累实战经验,能够快速开发需求和输出文档;而从需求开发和解决问题的经验中抽象出能力,无非就是在沟通时抓住重点,开发时做到极致,这也许就是院长所说的“复杂的事情简单化,简单的事情复杂化”;
    除了专业能力的提升,还应该具备产品侧思考的能力,对一个系统有深入的认知,能够独当一面,做到专精一个领域。
  3. 希望提升的1-3项核心能力项,计划如何提升
    1 快速学习的能力。通过自主学习/经验复用,辨别需求开发中问题的关键,快速掌握解决问题所需能力,不企图全面构建知识体系
    2 时间管理的能力。目前,需求开发中实际用于写代码的时间很少,大部分的时间用于串联和验证已知或猜测的信息,对齐上下游,这也许是因为对开发环境、业务的不熟悉和开发技能的缺失。另外,由于当前需求开发很依赖他人的讲解,做到高效提问,不耽误他人时间也是很重要的。以及要学会在开发中预留buffer,在实际开发中提高效率。
    3 精确表达的能力。描述调研方案或解释曾做过的功能时,往往没法在当时呈现所有的思考结果。在对齐需求和协同开发中,有时会抓不住重点,重复提问,效率不高。需要向同事请教如何提升这项能力。
  4. 上级总结
    24年成果:
    1)mops完成2个公有云的主机开/关机/重启自动化的能力研发
    2)完成数据库influxdb的备份和恢复任务开发
    做的好的:
    1)作为应届生有一定的主动性,对于不懂的积极学习
    2)能够在同学指导下完成工作
    待改进:
    1)技术能力需要加强和提升,需要快速学习新技能
    明确员工亟待提升的核心能力,并为其制定成长计划:需要提升代码开发基本技能,加强基础学习
  5. more thinking
    无法独立开发,效率低,代码量少;问人很正常,在群里问,卡点,同步领导;值班,锻炼解决问题的能力;长期发展,先补齐技能,有好奇心,,提高工作日效率,边工作边学习成长,,用心,真诚 :_
    阶段目标、、开发效率,高级开发,项目管理
    2024:校招,旅行,吉他
    2025:职场,矫正,足球
    【程序员如何快速成长,这几点值得重点参考,我只教一遍!】 https://www.bilibili.com/video/BV1bK4y1B7rj
    【【社区分享】程序员宝藏推荐!提升天花板!覆盖学生到架构师!】 https://www.bilibili.com/video/BV1Ta411s7ij/
    【建议收藏,高级开发如何提升产品能力!我常用的5个网站!】 https://www.bilibili.com/video/BV1y2C3YpEaL/

25.1.16 试用期转正答辩汇报

  • 尊敬的领导、各位同事:
  • 大家好!我是蔡枫,今天非常荣幸能在这里与大家分享我在试用期的工作成果和心得。【翻页】我将从试用期工作内容、工作改进点、下季度工作计划以及问题与建议,四个方面进行汇报。【翻页】
  • 试用期工作内容
    这段时间,我主要参与到两个项目中:分别是MOPS公有云自动化和InfluxDB服务化。
    我首先接触到Mops华为云和Azure两个云厂商公有云主机的 “开/关机/重启/申请/回收自动化” 的需求,在业务熟悉的同时,进行了云主机运维功能的完善。以及,我参与到influxdb服务化开发,目前实例备份与恢复功能已开发完成,并将逐步支持其他功能。【翻】
  • ;)
  • 试用期工作改进点
    在试用期间,我意识到需要首先要提高代码能力。在需求开发的过程中,我逐渐熟悉开发环境和流程,初步掌握验证和联调方法。作为团队和开发新人,在这个阶段希望能锻炼自己的专业能力,积累实战经验,能够快速开发需求和输出文档;
    同时,我补充了运维相关的能力,学习并通过实践掌握了K8s,Linux相关知识和基本操作,为后续工作打下坚实基础。
    以及在做需求,协同开发的过程中,我增强了沟通理解的能力,学会从需求开发和解决问题的经验中抽象出能力,要能在沟通时抓住重点,开发时做到极致。【翻】
  • 下季度工作计划
    接下来,我计划完善和支持InfluxDB实例集恢复、扩容、重搭等功能,以进一步提升系统的高可用性。
    总的来说,通过Kubernetes集群和Helm Chart等实现了对InfluxDB集群的高效管理。用户可以通过DataMars控制台方便地进行实例的备份恢复和配置变更,同时支持扩容重搭以应对业务需求的变化。工作流引擎负责处理用户请求并调用相应的Helm Chart模板、通用或专有的apiserver接口等,确保操作的自动化和一致性。相信有了前期开发经验,我能够更快的进行后续的开发产出。【翻】
  • 问题及建议
    最后,希望提出一些建议。首先,在新人指引方面,我认为前期培训缺少技术方面的指引,可能导致新员工在后续开发中理解比较费劲,上手难度大。建议组织开展包括开发流程、开发环境搭建、代码规范等内容的培训,确保新员工能够快速融入。其次,关于文档落实,我发现一些技术文档内容不够详尽,导致无法自行定位问题,需要频繁联系文档编写者。对此首先我应该提高自己的文档撰写水平,确保能够让人快速理解整体流程,找到问题所在,并及时更新文档内容。
  • 总结(的)来说,在各位同事的帮助下,我在试用期间得到了快速成长,收获颇丰。【翻页】以上就是我的汇报内容。感谢各位领导和同事的聆听和支持。我相信,在大家的共同努力下,我们的工作会取得更大的进步。谢谢大家!

1.16 InfluxDB服务化 :备份集恢复

1.02:不上心
1.14:开发uat自测(sit没测)完成,开发分支合dev提测,最后合main上线
1.16:SD/GA测试发版失败,恢复工作流需要人工介入验证,存在问题 1地址没有动态配置!! 2漏配接口/审批流变更
1.21:发布修复版本,生产延后

2.7

复工
当前阶段的关键,,交付能力,,工程能力是练出来的
熬夜是没有对明天的期待、、-》培养兴趣转移注意力、,books
程序员。技术。不要只看自己的一亩三分地。。开源项目
工作以外;:给自己创造需求,根据需求解决问题,在解决问题上配合看书,,从而在某一细分领域有知识图谱,有一技之长,用系统性的看书代替cdsn查找零散的解决方案
“下班的时间放在哪哪里就有提升”
副业?;web3;licai

2.13 DataMars服务架构

  1. apiserver:1 datamars管控接口 2 mcloud回调接口 3 properties获取apiserver服务自身暴露的域名端口+controller接口拼接url
  2. apiserver-{engine}:引擎专有服务,工作流(其实是workflowclient处理)中调用不同服务暴露的接口(common,,influxdb,,apiserver)
  3. bakserver/metaservice/xx-agent:其他服务,由rpc/grpc暴露服务
  4. 调用架构:api、v1、v2
  5. 服务架构:通过Kubernetes集群和Helm Chart等实现了对InfluxDB集群的高效管理。用户可以通过DataMars控制台方便地进行实例的备份恢复和配置变更,同时支持扩容重搭以应对业务需求的变化。工作流引擎负责处理用户请求并调用相应的Helm Chart模板、通用或专有的apiserver/k8srepository接口等,确保操作的自动化和一致性。

todo 手画图

2.18 InfluxDB服务化 :本地变配

2.6-2.16:现有InfluxDB集群实例(只考虑data节点)的CPU、内存和存储资源已无法满足需求,需对资源配置进行扩展,以提升性能和稳定性。通过修改StatefulSet中cpu、memory配置并删除Pod触发StatefulSet控制器重建data节点Pod以应用新配置,通过修改data节点Pod对应的pvc中storage配置以触发pv的存储扩容(只能增加),实现资源变配(本地变配)
2.17:前端对齐开发,准备进入sit联调
2.18:插入需求“meta节点自定义创建”
2.28:整合已知信息已读代码->无法理解多节点类型实例如何发起变配,应该果断求助
3.3-3.10:改造influxdb集群为父子实例模式,改配置,传参调试,适配已有功能,考虑存量实例的影响
3.11-3.13:data变配基础上开发meta变配,云管订单遇到配额不匹配问题,上线延期下周
3.14:跨团队求助无果,请求协助,,新需求着手开发
3.17:搁置,开发新需求
3.18:实操tidb复现该“问题”,考虑转向与云管沟通。。
3.27:发版 =》InfluxDB父子实例改造/本地变配/节点/实例重启

2.22

正畸, 启动
读书时无所事事的日子,今天拔完牙和妈妈一起冰敷等待的日子,还有多少
刚开始普遍很难,易的是背八股,难的是落实和推进
如何跳出这个困境?如何跳出程序员行业?30岁,35岁
熬夜是因为没有对明天的渴望。但是在晚上的当下,有很多事情想做😿
喜欢一个人独处,是因为不想自己长期以来形成的情绪稳定被打破。害怕形成亲密关系,有时无法融入团体😿

2.27 InfluxDB服务化 :实例创建自定义Meta节点规格

2.18:本需求作为其他需求开发的前置条件
2.19:apisever打log上uat调试创建流程,从已部署分支git branch新分支以免影响正在使用者
2.21:提sql变更 1 dataspace提工单 2 直接进入各环境metadb(其本身为容器部署的mariadb服务) 3 某些配置项可通过datamars控制台修改
2.25:云管运营端商品信息变更,考虑是否影响存量实例;;1 云管释放旧实例将计价报错->调datamars管控接口释放/发版前释放旧实例 2 可以发起工单但无法下单->手动修改配额发起工单后过云管审批
2.27:发版流程、、代码合master,流水线打包使用(发版版本)部署,sql变更(定时,增加条件避免误订正),云运营变更(改一次console-cloud即各环境共用)
Steven👨‍🦲:裁员,残酷,危机感,,工作就是生活的很大一部分

  1. meta规格,配置到instance_spec,engine = “InfluxDBMeta”
    | 场景 | 规格代码 | CPU 核数 | 内存 (GB) | 存储 (GB) | 网络带宽 |
    |————–|———————–|———-|———–|———–|————|
    | 小型集群 | influx-meta-small | 2 | 4 | 50 | 中等 |
    | 中型集群 | influx-meta-medium | 4 | 8 | 50 | 高 |
    | 大型集群 | influx-meta-large | 8+ | 16 | 50 | 超高 |
    | 特大型集群 | influx-meta-xlarge | 16+ | 32 | 50 | 超高 |
  2. apiserver实例创建逻辑
    meta信息通过request.getExtraJson()传入,ClusterInstanceService#initClusterInstanceExtend保存在cluster_instance_extend;initClusterInstanceParams能适配保存meta节点的param吗(iops,连接数,influxdb不考虑?)
    不考虑在cluster_instance要新增字段体现meta规格信息
  3. apiserver-influxdb工作流完成实例创建,pv,sts,helm,install,node,instance
    获取meta规格信息,构造临时value文件,生成用于安装InfluxDB集群的Helm命令并执行
  4. 以上只是在k8s环境中实现了自定义meta规格创建,事实上应该参考tidb,将influxdb集群改造为父子实例模式,逻辑上把data/meta节点区分开,进行变配/更新meta信息..
  5. more to concern? param创建,变更?extend父子实例数量不一致?实例创建流程,sts,副本,顺序。。

3.3 阶段目标 :入职半年 ..

deepseek:数据库方向是一个值得长期投入的领域,尤其适合对系统底层感兴趣的程序员。你的现有经验(运维+K8s)可成为切入云数据库或分布式数据库的跳板。建议以​“运维需求驱动内核学习”​为短期目标,逐步掌握分布式一致性、存储引擎等核心技术,同时通过开源贡献和项目实践构建技术影响力。=> https://yuanbao.tencent.com/bot/app/share/chat/c6b48985efa0c1101e5c6ae18c867724
rong teng:在midea得到的成长是显著的;(身兼开发运维多职,具体求职情况如何?)数据库方向有些窄;(作为senior求职需要专精时显得窄?作为基础能力学习可行?)应届生可以提转方向,转团队;以招聘市场心仪岗位的需求作为努力发展的方向!?

3.13 AI编程助手:codetip

代码可以看作是一种特殊的prompt,用于引导AI生成高质量的代码提示;开发者有目的地编写代码,实是为AI提供清晰的意图上下文。
代码补全 需要让AI理解意图;技巧 提供具体示例,遵循规范和语义化,规范注释;“内联对话”
代码对话 本地工程理解@workspace,RAG增强
最佳实践

todo 体验cursor

3.17 InfluxDB服务化 :实例/节点重启

3.17:简单需求,父子workflow + k8s资源控制器
3.18:接口配置:接口信息查看mariadb已有的相同接口,其他信息参考influxdb自身的其他接口;前端联调完成
3.19:提测,发版:发版分支一周内进行 1 代码扫描-安全扫描 2 安全-软件成分-Web漏洞-灰盒,解决漏洞;代码仓库设置发版分支,史诗中关联所涉及仓库,检索其发版分支的扫描报告,手动关联web漏洞测试报告,质量门禁达标以通过安全卡点

3.24 从零实现Kubernetes环境下的InfluxDB自动化登录工具:Bash与Java的跨语言实践

  • 背景与需求分析
    在云原生环境中,InfluxDB集群常以StatefulSet形式部署于Kubernetes。运维人员需要频繁执行以下操作:
    动态选择特定Data Pod;解密存储在Secret;通过交互式命令登录数据库。
    手工操作存在效率低下、易出错等问题。本工具通过Bash脚本整合Kubernetes CLI、Java加解密等能力,实现全流程自动化。
  • 技术方案设计亮点
    1. 混合编程模式(Bash+Java)
      核心难点:GCM解密在纯Bash环境难以实现(openssl版本低(不会升级…) 没有aes-256-gcm工具)
      创新方案:编写Java脚本,动态生成Java解密类(图1),通过Java标准加密库实现AES-GCM解密
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 生成随机类名避免冲突
      CLASS_NAME="InfluxDecryptor_$(mktemp -u XXXXXXXXXX | tr -dc 'a-zA-Z0-9')"
      # 编译并执行Java代码
      if javac "$TEMP_JAVA"; then
      auth_output=$(java -cp /tmp ${CLASS_NAME})
      echo ""
      echo "$auth_output"
      echo ""
      else
      echo "Java编译失败"
      return 1
      fi
      凭证安全处理机制:
      • 使用临时文件存储解密代码(TEMP_JAVA="/tmp/${CLASS_NAME}.java"
      • 执行后立即清理编译产物(rm -f "$TEMP_JAVA"
      • 避免敏感信息持久化
    2. 跨语言参数传递
      动态生成的Java脚本中,选择标准输出+格式控制方案:
      1
      2
      System.out.println("USERNAME:" + username.trim()); 
      System.out.println("PASSWORD:" + new String(decrypted).trim());
      多行输出解析难题
      1
      2
      # 错误示例:初始方案采用`IFS`分割导致变量截断
      IFS=: read username password <<< "$credentials"
      优化方案:bash脚本中,使用sed精确提取java脚本输出,通过正则表达式过滤前后空格,避免不可见字符影响
      1
      2
      3
      4
      5
      6
      7
      8
      credentials=$(get_auth $1) # get_auth()动态生成java解密脚本并多行输出
      if [ $? -ne 0 ]; then
      echo "解密失败,无法登录"
      exit 1
      fi
      # 提取用户名和密码(处理多行输出)
      username=$(echo "$credentials" | sed -n 's/^USERNAME://p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
      password=$(echo "$credentials" | sed -n 's/^PASSWORD://p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
    3. 交互式Pod选择器
      1
      2
      3
      4
      5
      6
      7
      8
      9
      function select_data_pod() {
      # 过滤带data标签的Pod
      PODS=$(kubectl get pod -n $NAMESPACE | grep "data" | awk '{print $1, $6}')
      # 构建交互式菜单
      select pod_option in $PODS; do
      SELECTED_POD=$(echo $pod_option | awk '{print $1}')
      break
      done
      }
    4. 传递方案对比:
      方案 优点 缺点
      文件存储 实现简单 存在安全风险
      环境变量 进程内可见 长度受限
      标准输出 无持久化风险 需严格格式控制
      网络传输 适合分布式 增加复杂度

      本项目完整代码已开源,读者可通过GitHub仓库获取最新版本。在Kubernetes运维领域,通过灵活组合各类工具实现自动化,是提升效率的关键路径。

3.28 InfluxDB服务化 :节点迁移 / 重搭

3.28:需求分析->将pod迁移到集群的另一个资源充足的node上,并且恢复数据以及集群功能(元数据)
3.31-4.1:存量实例问题处理,, 建议客户使用改造后的influxdb实例,存量实例释放/启停/重启等功能遇到问题 =》未考虑好适配
4.2-4.7:出方案 1 sts指定亲合度规则以在指定node重建pod和pvc 2 开源influxdb-cluster功能不完全支持,考虑从分片副本层面恢复数据
4.8-4.10:数据恢复的主要思路=》从健康节点的分片副本copy-shard恢复出迁移节点原有的所有分片副本,?质疑->恢复过程中健康节点的分片副本持续写入的增量数据能否恢复??
4.11:考虑创建第3个Data-Pod替代迁移节点(从仍健康的迁移节点上迁移数据)× ->应该认为原节点完全不可用(相当于节点重搭了)设计方案参考其他数据库产品;;
4.14:推方案不动..自验证copy-shard达到预期效果,但无法解答原理,,自测方式还需模拟实际场景,多线程写入。。
4.15:写bash脚本批量写,开多终端模拟多线程,分片副本达70M+大流量2000point/s写 =》copy-shard恢复出分片副本与健康节点持续写的副本md5值不一致,恢复副本export文件小,猜测丢失数据
4.17:InfluxDB Data节点迁移方案评审:先迁移后逐个恢复分片数据。已验证在分片副本大小70M、写入数据达2000point/s的情况下直接copy-shard会导致增量数据丢失,考虑在copy-shard前先执行truncate-shards截断热分片(集群中所有写入最新数据的分片,截断后关闭写入,变成冷分片),并在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了全部原有分片的新热分片副本,最新数据写入这个副本,然后再逐个从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复;自测符合预期
4.18-4.21:开发。发现原checkPodRebuildReady接口有时不符合预期,执行influxd-ctl指令(硬写成String在代码中)有时失效(meta no leader..),经常需要手工介入。。
4.22:提测
4.23:InfluxDB开源版可靠性不确定,,社区版,单节点,,开发,值班暂停
官方文档 https://influxdb-v1-docs-cn.cnosdb.com/influxdb/v1.8/introduction/install/
开源influxdb-cluster源码 https://github.com/chengshiwen/influxdb-cluster

Data节点迁移方案

  1. 重启所有meta节点,避免后续执行influxd-clt指令时报错“no leader”
  2. 记录待迁移Data节点上的分片副本信息
  3. 迁移节点,sts加affinity节点调度规则,重建pod和pvc,完成后恢复sts
  4. 分片数据和元数据完全丢失,backup工具缺失,考虑逐个恢复分片副本
  5. 迁移节点重新加入集群 influxd-ctl remove/add-data
  6. 截断热分片truncate-shards(集群中所有写入最新数据的分片),在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了原有分片的热分片副本,新数据写入这个副本
  7. 再从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复
  8. 检查分片恢复情况

故障矩阵:

  1. 架构:2 data 3 meta 副本因子为2
  2. 挂1个data -> 节点迁移、重搭
  3. 挂2个data -> 备份恢复
  4. 挂1个meta -> 未知
  5. 挂两个meta -> 未知
  6. 挂三个meta -> 未知

4.14 DB值班开始 ; )

DBCLOUD开源DB报警群(实例),DBEngine告警群(机器),致命告警->数据库值班告警处理群,MariaDB/MySQL/MongoDB/PostgreSQL 常见问题,,

  • MariaDB容器数据盘使用率过大
    1.异常内容:容器数据盘占用率超过90%
    2.问题定位:
    1)/var/lib/mysql 目录下存在大量临时文件(如 #sql_1_44.MAD,大小达185G)
    2)show process查看未提交的长事务(WITH RECURSIVE 查询和 Sending data 状态,确认这些递归查询正在生成大临时表)持有临时表资源,导致文件无法自动清理。
    3.处理方案
    1)KILL 15443008, 15443009, 15443010; // 终止进程(替换为实际ID)临时kill了超长事务连接,临时文件自动清理了(/var/lib/mysql挂载点空间得到释放)
    2)联系用户优化sql
  • MariaDB实例备库IO线程停止
    1.告警内容
    实例 IO 线程停止.通常由于无法连接到主库.
    2.问题定位:
    1)服务可用性->观察到发生主从切换,发生时间符合告警情况
    2)kubectl get pod -n mariadb -Lrole -Lhealthy->从库(原主库切换而来)健康为no
    3)kubectl descirbe 从库pod,观察到mysql container发生terminated,OOMKill,,
    4)监控指标:内存缓慢提升->考虑扩容,暴增->dataspace诊断看慢sql
    3.处理方案
    1)备库重搭
    2)联系用户扩容/优化慢sql

4.22 2025成长对话

  • 制定2025年度重点工作计划。
    快速开发InfluxDB服务化需求,持续优化已有功能;深度熟悉开源数据库,确保提供稳定服务。对齐其他数据库产品,了解用户实际需求,多方面考虑设计方案。做好值班任务。

  • 希望提升的1-3项核心能力项,计划如何提升。
    1 深入技术栈学习。阅读InfluxDB源码,学习数据库架构设计,K8s应用课程等,掌握高频业务场景的原理和运维技巧。
    2 高效合作开发的能力。在全面思考,明确需求后开始开发,遇到卡点快速解决。

  • 面向未来1年的职业规划。
    本岗位沉淀
    在这个阶段希望提高自己的工程能力,能比较全面地思考设计方案,快速开发和交付需求;还应该具备产品侧思考的能力,对一个系统有深入的认知,能够独当一面,做到专精一个领域。

4.23 Mops需求:NBU备份自动化

4.25-5.8:手动验证全流程+调通API,分阶段做,卡点及时同步到群聊.. 官网找接口文档/厂家提供,postPolicies接口参数调不好,就先手动设好用get查出来构造requestBody..
5.9-5.13:理清自动化接入和改造方案(先看一期代码,考虑接口和表能否复用),主动拉评审会议
5.14-5.16:开发及时理清需求原型和改造点,开发备份域配置管理界面
5.19-5.23:重难点=》作业平台下发备份客户端安装ansible脚本改造,根据传参when指定不同task,实现在target主机安装指定平台的client,expect实现交互式流程
5.26-5.29:NBU备份申请自动化开发,实现BackupNbuService(备份平台NBU涉及代码,构造RestTemplate调API);备份域配置“增删改查”,分页,模糊查询,@Transation处理先删后插/先改后改/先删后删。。延期->0605
6.3:自测=》页面上发起请求获得requestBody/通过postman调用本地起的后端服务,调试=》注释排查法/计算器debug
6.4:发sit,延期->0612;完善todo
6.5:自测,用户验收,ddl&dml(注意StringEncryptor加密的密钥随env变化!!) 发版,存在问题待完善

  1. 统一运维平台:就是接各种需求,把主机创建、备份等操作自动化
  2. Ansible入门:脚本在特权机上,指定targetIp执行,,playbook为入口,roles/tasks实现具体逻辑,,安装client注意预检查,常量写在var文件,脚本写在flie/script.py通过scp传到target机器执行
  3. CRUD基本功:1. 分页,baseMapper.selectPage(new Page<>(page, size), getQueryWrapper(req)) 2. 模糊查询,queryWrapper.eqIfNotNull(BackupConfigPO::isDeleted, 0).likeIfNotNull(BackupConfigPO::getConfigKey, condition.getBackupRegion()) 3. 模糊查询条件涉及表中text类型字段的内容为json,本质还是处理wrapper,queryWrapper.apply(“JSON_SEARCH(config_desc, ‘one’, ‘%” + req + “%’, NULL, ‘$.req’) IS NOT NULL”);

5.5 02/2027

  • 工作经历
    美的 后端开发工程师-Java 2024.7~
    • Mops自动化运维平台:提供云主机/存储自动化运维功能
      1. 接用户需求,调研厂商api,实现了华为/azure云主机开关机申请回收,netbackup备份自动化
      2. 了解整个系统架构,springboot,,mq,java回收器,,
    • DBengine数据库引擎平台:提供数据库运维管理功能
      1. 调研开发基于开源influxdb的服务化功能,对齐mariadb,提供创建回收、备份恢复、重启、节点迁移等功能
      2. 具备常见数据库运维能力,,oom,慢sql,主从切换异常,,
  • 项目经历
    1. 读influxdb源码?
    2. 读《马斯克传》后感:.. 读《阿里巴巴Java开发手册》..
  • 技能
    1. Java开发
    2. 数据库

6.9 2025年中总结

  • 工作成果
    1.InfluxDB服务化体系构建
    完成父子实例模式改造,新增InfluxDBMeta规格体系,实现节点级独立变配能力,为后续节点重启、迁移奠定基础;
    针对Data节点迁移提出 “热分片截断-冷副本恢复”双阶段法,通过多线程大流量写入压力验证(2000 points/s),解决开源工具增量数据丢失问题;
    参与数据库运维工作。
    2.NBU备份自动化开发
    支持备份平台自动化运维需求,开发基于Ansible的客户端安装脚本,整合NBU API,实现备份申请自动化;
    开发备份域配置管理界面(增删改查+JSON字段模糊查询)
  • 能力提升
    在InfluxDB服务化需求开发中锻炼了“场景抽象-方案验证”的能力,从寻找开源社区方案到定制化能力开发(如备份集恢复、节点迁移及数据一致性保障),梳理故障矩阵(如单Data宕机、多Meta宕机的应对策略);在NBU备份自动化需求开发中能够快速上手Ansible脚本,复用已有能力,与用户及时沟通并完成开发。
  • 存在不足
    数据库开发方面需要积累技术深度,全面考虑方案并推动评审;自动化运维需求开发可以积累解决方案,缩短交付周期。

6.12 智能体人才认证(一级)

1. ChatGPT的基本原理及应用实践分享
  • what is 大预言模型
    流式输出(逐字计算概率),基于Transformer神经网络(本质上一个Encoder+Decoder结构,自然语言 ⇄ 机器理解)
  • why ChatGPT
    除了卷大模型(参数量)&大数据量,有着更好的交互原因:1 指令微调? 2 基于人类反馈的强化学习
  • 提示工程 Prompt Engineering
    提示词尽量简单、明确,最好完整描述以下关键要素:1 指令 2 上下文 3 输入数据 4 输出指示
    提示词使用技巧:1 明确提出(不)应该做什么 2 提供输出的格式提示 3 使用特殊符号指令将需要处理的文本分开 4 增加示例,少样本提示 5 增加任务角色(Role)或场景
  • 应用场景实践
    本地知识库问答:从本地知识库构成的文本向量库中搜索相关知识+用户问题 =》一个提问(增强Prompt 如:“基于以下知识:{text1}…{textN},回答:{question}”)=》 LLM(如 ChatGLM2、GPT-3.5)读取该 Prompt结合自身语言能力生成最终回答;模型参数提供语言能力,但不存储动态知识。语言模型的“理解能力”本质是参数化的统计规律,通过海量通用文本训练获得。专业领域适配需针对性选择微调或 RAG 策略,二者互补而非互斥。当前技术趋势是:通用大模型作“引擎”,领域知识库作“燃料”,Prompt 工程作“方向盘”,三者协同实现高效、低成本的专业化智能问答。
对大模型的思考
  • 大语言模型 LLM 的“理解能力”来源:参数、训练与概率生成
    1. 60亿参数的本质
      参数是什么?神经网络中神经元连接的权重值(浮点数矩阵),例如 ChatGLM2-6B 的 60 亿参数即其网络权重总量。
      参数如何产生?通过海量无监督预训练:模型从数万亿 token 的通用文本(网页、书籍、百科等)中学习语言统计规律。例如:GPT-3 训练数据:45TB 原始文本 → 过滤后 570GB,包含近万亿 token;训练目标:预测文本中遮蔽词(如 “猫喜欢抓__” → “老鼠”)或续写句子。
    2. 参数如何实现“理解”?
      概率建模:LLM 本质是概率生成器。给定输入文本,模型计算下一个词的概率分布(如 “天空是___” → “蓝色”概率 80%,“绿色”概率 0.1%);
      上下文编码:通过 Transformer 的自注意力机制,模型捕捉长距离依赖(如代词指代、逻辑关联);
      知识内化:训练中高频出现的知识(如 “水的沸点是 100°C”)被编码到参数中,形成“通用知识库”。
    3. 训练成果与参数的关系
      训练完成后的参数 = 固化后的语言规律与知识表示;
      生成过程:根据输入 Prompt 的语义,激活相关参数路径,按概率生成符合语言习惯的文本。
  • 专业领域模型训练:微调 vs. 知识库增强
    1. 全参数微调(Full Fine-tuning)
      方法:在领域数据上继续训练模型,更新全部参数(如用医疗文献训练 ChatGLM2);
      效果:模型深度内化领域知识,生成更专业、连贯的文本;
      成本:需大量领域数据(GB 级)和 GPU 算力(如 8×A100 训练数天)。
    2. 高效参数微调(PEFT)
      方法:仅训练少量新增参数(如 LoRA、Adapter),冻结原模型参数;
      优势:节省 90% 算力,适合中小机构;
      适用场景:领域术语适应(如法律条文格式),但无法新增未训练过的知识。
    3. 知识库增强(RAG)的定位
      核心价值:无需训练模型,直接注入动态更新的领域知识(如企业最新产品文档);
      局限:依赖检索质量,复杂推理能力受限于 LLM 本身。
  • DeepSeek 之所以能广泛回答各领域问题,并非因为对所有领域都做过“专门训练”,而是通过大规模通用预训练 + 领域增强技术 + 智能调度机制实现的;
    1. 基础:海量通用预训练(广度覆盖)
      DeepSeek 的底层模型(如 DeepSeek-R1)在训练初期使用数万亿 token 的互联网公开文本,覆盖科技、教育、历史、文化、生活、基础学术等广泛领域。
      效果:模型能对大多数常识性问题生成合理回答,类似一个“受过通识教育的聪明助手”。
    2. 增强:垂直领域优化策略(深度强化)为提升专业领域表现,DeepSeek 采用以下技术实现“泛中求精”:
      混合专家模型(MoE):模型内部划分多个“专家子网络”(如医疗、法律、编程等),根据问题自动激活相关专家;
      领域微调(Fine-tuning):对金融、法律、医学等专业领域,用高质量数据二次训练模型,优化参数
      检索增强生成(RAG):对动态知识(如实时政策、企业数据库),通过外部知识库检索最新信息,再生成答案;
    3. 调度:智能路由与知识管理
      动态路由机制:用户提问时,模型自动判断问题类型,分配至: 通用知识层(如“水的沸点是多少”);专业模块(如“心肌梗死的最新诊疗指南”); 外部检索(如“2025 年光伏产业新政策”)。
      知识更新与纠偏:用户反馈可修正错误答案(如律师指出法律条文解读偏差); 结合知识图谱持续更新事实库,减少“知识过期”问题。
    4. 用户建议:如何获得更专业回答?
      明确领域身份: 提问时声明“以金融分析师身份,分析光伏产业趋势”,引导模型调用专业模块。
      开启深度思考模式: 对逻辑问题(如数学、编程),勾选“深度思考(R1)”提升推理质量。
      补充专业资料: 上传领域文档(如论文、手册),用 RAG 增强答案准确性。
      微调定制专家: 企业用户可通过 LoRA 微调,训练专属领域模型(如“医疗问诊助手”)。
2. 大模型提示词工程基础
  • 提示词基本要素
    1. 指令:想要模型执行的特定任务或指令
    2. 上下文:包含上下文信息,引导模型更好地响应
    3. 输入数据:用户输入的内容或问题
    4. 输出指示:指定输出的类型或格式
  • 提示词工程进阶技术
    1. 少样本提示:可以作为一种提示词,以启用上下文学习,我们在提示中提供演示以引导模型实现更好的性能。
    2. 链式思考(CoT)提示:提出问题的同时提供自己的推理方法,供LLM学习参考
    3. 检索增强生成(RAG):如本地知识库问答。从本质上讲,RAG包括一个检索组件、一个外部知识数据库和一个生成组件。整体流程:RAG需要从外部知识数据库中获取文档,然后将这些文档与用户的查询一起被传输到LLM,用于生成响应
    4. 自动推理并使用工具 (ART):?接到一个新任务的时候,从任务库中选择多步推理和使用工具的案例。在测试中,调用外部工具时,先暂停生成,将工具输出整合后继续接着生成。
    5. 自我反思(Reflexion):?自我反思是一个通过语言反馈来强化基于语言的智能体的机制。
      • 在高层次上,自我反思将来自环境的反馈(自由形式的语言或者标量)转换为语言反馈,也被称作 self-reflection,为下一轮中 LLM 智能体提供上下文。
      • 这有助于智能体快速有效地从之前的错误中学习,进而提升许多高级任务的性能。
3. 如何从0-1开展Prompt工程项目
  • Prompt就是给AI的指令,引导大模型生成响应回答。
    进阶例子:“现在你是一名xx专家,以下是xx内容,你的任务是对内容进行xxx,让我们一步一步做:1. 做/不做xx 2. 以json格式输出… 3. …”
  • 什么是Prompt工程?
    是业务服务应用大语言模型的中枢,用于释放LLM的能力,包括 1)单个任务的Prompt撰写调试 2)多个任务Prompt的设计组合
    如:撰写短视频文案 =》 1. 分析热门文案 2. 结合商品信息生成文案
  • Prompt工程开展方式
    根据生成的可行性与效果,明确对模型的输入、输出目标,经设计/调试后,生成效果达成业务目标后,固化为应用服务。
一个尝试:生成编程指令Prompt
  • 你是一个资深编程助手,擅长根据用户诉求生成可执行的编程指令。让我们一步步做:
    1. 将用户诉求拆解为清晰、可执行的原子化步骤。
    2. 注意环境适配(例如 Linux/macOS 环境优先使用 Bash/Python,Windows 环境优先使用 PowerShell)以及进行包依赖的检测。
    3. 考虑错误处理,如网络请求需设置超时(例如 ‘timeout 3’)
    4. 输出规范,代码必须包含完整可执行结构和注释(每20行至少1个注释)
    5. 按照以下格式返回:{步骤拆解:””,生成程序:””}
    6. 不受示例语言的限制,选择最适合的语言实现。
  • 这是一个示例回答
    输入:写一个能够在Linux环境使用的脚本,检测对指定IP和端口是否连通。
    输出:
    {
    步骤拆解:
    1.参数检查:验证输入参数格式(IP 端口 或 -f 文件)
    2.端口测试:依次尝试 nc → telnet → /dev/tcp 三种方式(3秒超时)
    3.批量处理:支持从文件逐行读取 IP 端口(跳过空行和注释)
    4.结果输出:直接显示开放/关闭状态
    生成程序:
    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
    #!/bin/bash -e
    # 端口检测工具v2 | 支持: Linux/macOS/Windows(WSL)
    # 基础依赖: nc > telnet > /dev/tcp

    config_timeout=3 # 全局超时可配置

    # 函数: 环境预检
    check_dependencies() {
    # 检测网络工具链
    local tools=(nc telnet)
    for tool in "${tools[@]}"; do
    if ! command -v "$tool" &>/dev/null; then
    echo "! 缺少 $tool, 启用降级模式" >&2
    return 1
    done
    done
    return 0
    }

    # 函数: 端口检测 (带双超时)
    test_port() {
    local ip=$1 port=$2
    # 方案1: nc (Linux/macOS)
    if nc -z -w "${config_timeout}" "$ip" "$port" 2>/dev/null; then
    return 0
    # 方案2: /dev/tcp (跨平台)
    elif timeout "${config_timeout}" bash -c "cat < /dev/null > /dev/tcp/${ip}/${port}"2>/dev/null;then
    return 0
    fi
    return 1
    }
    # 主流程
    [ $# -lt 2 ] && echo "用法: $0 IP 端口" && exit 1
    check_dependencies || echo "! 使用基础检测模式" >&2

    if test_port "$1" "$2"; then
    echo "✅ $1:$2 开放"
    else
    echo "❌ $1:$2 关闭" && exit 3
    fi
    }
  • 这是用户的正式诉求
    输入用户诉求。

6.27 Mops需求:负载均衡变更自动化

6.27:页面流程理解+看“申请”自动化代码->考虑改造点,找api,输出文档
6.30-7.4:需求对齐,减少开发量的机会,,对于依赖项的变更(healthCheck->ResourcePool-VirtualServer)采用“蓝绿发布”=》先增后删
7.5-7.9:开发完成
7.10-7.11:自测,前端发起一次请求F12取payload / 后端接口处打印入参 + 按需修改 =》构造入参,走通流程,只需关注新加的代码(无关逻辑如校验/审批代码可以先注释掉。。)
7.14:变更成功后回滚。。手动修改+提sql同步配置;思考=》是不是可以直接另提一次变更,或者另外提供回滚接口,,总之此时回滚和前一次变更已经没有关联

  1. f5理解:F5 BIG-IP 是业界主流的硬件/软件负载均衡解决方案。配置好负载均衡后,一个请求从客户端到后端服务器的生效流程涉及多个关键环节,其核心是​​客户端访问域名 → DNS解析至F5的VIP → F5转发请求至Pool Members → 后端处理并返回响应
  2. Mops服务架构。。用户提交工单-> iflow审批流-> 回调/前端调接口-> 传入xxTicketVo-> convert成Dto/Po处理业务逻辑
  3. 蓝鲸。。统一运维平台竞品
  4. 自动化运维开发总结:工单,,提效,,用户,,,

6.28 Alibaba Java开发手册学习

2. 面向对象

解析值。。序列反序列parse,,JSONNode,,

3. 代码风格

  1. 魔法值 => enum
  2. 变量一般以小驼峰格式命名,但有一种特殊情况:定义类成员变量时,特别是POJO类中,针对布尔值类型的变量不要以“is”开头,而是将数据表中的“is_xxx”字段映射到POJO类中的属性“Xxx”(如is_deleted =》Deleted)
  3. 文档注释 /** */ 加上创建和修改时间,写在代码上方,, Idea怎么配置??

5. 异常与日志

  1. where to throw Exception?who to solve?how solve?
    如果异常在在当前方法的处理能力范围内且无需透出,就直接捕获异常并处理;否则向上抛出,由上层方法或框架来处理。
    如果在方法内部处理异常,需根据业务场景定制处理,如重试、回滚(还有 ticketDetail.setExecutionStatus(xxEnum.FAIL) 返回)等操作;如果向上抛出异常,需要在异常对象中添加上下文参数、局部变量、运行环境等消息,便于排查问题。
    考虑设计业务逻辑,无论在哪一步终止业务,都能让外界感知此时的状态,并保留错误信息(ststus,log)
  2. 异常分类
    • Error(致命异常),不可控错误,如StackOverFlowError、OutOFMemoryError
    • Exception(非致命异常)
      • checked异常(受检异常)例如:IOException, SQLException。
        • 无能为力型,如SQLException,只好保存现场人工介入
        • 力所能及型,如发生非授权异常可跳转权限申请页面
      • unchecked异常(非受检异常)是运行时异常,继承自 RuntimeException,更像是由 业务逻辑可能导致的异常
        • 可预测异常,如IndexOutOfBoundsException, NullPointerException,应该提前做好边界检查而不是抛出
        • 需捕获异常,如Dubno框架进行RFC调用时产生的超时异常DubboTimeoutException,客户端不能因服务端异常导致不可用,可以重试或降级处理
        • 可透出异常,如Spring框架中抛出的NoSuchRequestHandingMethodException,框架会自行将异常映射到合适的状态码如404
  3. throws关键字用于声明一个方法可能抛出的受检异常(checked)。BusinessException 通常是一个运行时异常,也就是非受检异常,不需要在方法签名中用 throws 声明(显式捕捉和处理),因为它通常表示业务逻辑错误,而不是程序错误。非受检异常的设计目的是让开发者在编写代码时不必显式地捕获或声明它们。
    但是,仍然需要确保在适当的地方捕获和处理,特别是在应用程序的边界层(如控制器层)进行统一的异常处理。??
  4. ​​防御式编程,可以让方法返回null,,防止空指针异常(NPE)上调用方的责任,需要事先判断
  5. 需定位报错行数 → 必须打印 e​​(传入异常对象)会输出完整的堆栈(e.printStackTrace())跟踪,包括类名、方法名、文件名和行号。
    ​​仅需错误描述 → 使用 e.getMessage()​​(适用于前端提示或自定义消息)。
    1
    2
    log.error("查询VirtualServer信息异常:"+e.getMessage(), e);
    throw new BusinessException("查询VirtualServer信息异常:" + e.getMessage());

7.11 CMDB项目深度参与策略

接手CMDB,, 做需求=》做系统
点击查看元宝的回答 https://yuanbao.tencent.com/bot/app/share/chat/920NVAbsqwtm https://yuanbao.tencent.com/bot/app/share/chat/SzQrsGlsuz88

🔧 技术攻坚重点

  1. 理解CMDB系统本质与行业实践**
    • 核心价值:CMDB(配置管理数据库)是IT服务的“中枢神经”,存储所有配置项(CI)及其关系,支撑运维自动化、变更管理等场景。
    • 主流竞品参考
      • ServiceNow CMDB:企业级SaaS方案,强在ITSM流程集成。
      • BMC Helix:传统ITOM厂商方案,适合复杂环境。
      • 开源方案:iTop、CMDBuild(适合二次开发,与你项目更匹配)。
    • 自研系统关键模块:数据采集 –> 数据建模 –> 关系拓扑 –> API服务–> 可视化平台
  2. 通过CMDB项目深挖技术栈**
    • Java底层实践场景
      • 高并发设计:自动发现引擎需处理海量设备扫描(学习Netty异步IO、Disruptor队列)。
      • JVM调优实战:配置项关系计算需避免Full GC(分析G1日志,调整Eden/Survivor比例)。
      • 分布式事务:CI数据跨服务同步(基于Seata AT模式或消息最终一致性)。
    • 云原生技术落地
      • 容器化部署:将CMDB模块拆分为微服务,用Docker打包,K8s调度(学习Pod滚动升级、HPA弹性伸缩)。
      • 服务网格治理:通过Istio实现API鉴权、流量控制(Envoy代理注入)。
      • 可观测性建设:集成Prometheus监控API延迟,Grafana展示拓扑关系健康度。
  3. 避免“CRUD开发”,聚焦架构能力**
    • 数据模型设计:主导CI分类标准制定(如主机/网络设备/应用的属性继承关系),用图数据库(Neo4j)存储拓扑。
    • 自动化采集框架:开发插件式采集器,支持Agent/API/SNMP多协议,用Quartz调度扫描任务。
    • 变更影响链分析:实现“修改防火墙策略 → 自动标记依赖服务”的联动逻辑(学习规则引擎Drools)。

💎 以CMDB为杠杆撬动技术纵深

  • 项目价值:CMDB是运维体系的“核心数据枢纽”,其复杂场景(数据一致性、分布式调度)是修炼Java底层和云原生的绝佳沙盒。
  • 技术抓手
    1
    2
    3
    4
    5
    6
    7
    8
    journey
    title CMDB项目技术成长路径
    section 基础能力
    数据建模 --> 领域驱动设计
    采集框架 --> 异步编程/协议解析
    section 高阶能力
    拓扑计算 --> 图算法/分布式事务
    云原生部署 --> K8s/Istio治理

📊 项目经验如何写入简历

简历模块 平庸写法 高价值写法(量化+技术关键词)
项目经验 参与CMDB需求开发 主导CMDB数据模型重构,设计弹性CI架构,支持200+动态属性扩展,模型变更效率提升40%
技术亮点 使用Spring Cloud开发API 基于Quartz+Netty开发高并发自动发现引擎,单节点支持5000+设备/秒采集
业务价值 提升系统稳定性 通过拓扑影响分析模块,故障定位时效从30min缩短至5min,年止损运维成本200万+

💡 简历筛选口诀:“技术深度×业务影响”双突出——避免写“增删改查”,聚焦架构设计、性能优化、跨系统集成。

毕 业 设 计

选 题

基于内地与香港金融平台的在线量化投资的数据原型系统的设计与开发__

论文结构

摘 要 II
Abstract III
目 录 V
第一章 绪论
1.1 引言
1.2 研究背景
1.3 研究现状
1.4 课题来源及意义
1.5 论文结构
第二章 数据原型系统开发环境和工具
2.1 Python爬虫
2.2 Django框架
2.3 设计模式
2.4 前端开发框架
2.5 开源图形库 ECharts
2.6 本章小结
第三章 数据原型系统架构设计
3.1 综合性金融系统架构设计
3.2 数据原型系统架构设计
3.3 功能接口设计
3.4 数据库设计
第四章 数据原型系统的实现
4.1从金融平台获取数据实现
4.2 数据库查询实现
4.3 数据可视化实现
4.4 Echarts 图形库渲染生成统计图表
4.5 数据下载接口实现
4.6 数据维护接口实现
第五章 数据原型系统的测试及结果
5.1 数据更新模块测试
5.2 数据下载模块测试
5.3 数据分析模块测试
5.4 数据维护模块测试
5.5 本章小结
第六章 结论
1.论文工作总结
2.工作展望
参考文献
致谢

数据准备

  • 香港市场股票数据库的构建
    • MySql数据库:股票、基金、期货
    • 数据库表结构:参考旧论文/直接用东方财富网表头
    • 用python爬虫爬取数据, 数据清洗, 爬取历史数据
  • 获取香港期货代码 \spider\Get_Future_Info.py
    • 数据库创建
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      class CreateDatabase:
      def __init__(self, new_database):
      self.new_database = new_database
      def create_new_database(self):
      connection = pymysql.connect(host='127.0.0.1', user='root', password='078114', cursorclass=pymysql.cursors.DictCursor)
      new_database_name = self.new_database
      cursor = connection.cursor()
      cursor.execute(f"CREATE DATABASE {new_database_name}")
      cursor.close()
      connection.close()
    • 数据库操作
      1
      2
      3
      4
      5
      6
      7
      8
      9
      class Database:          
      def InsertFutureInfo(self, item): # 插入数据
      mydb = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='078114', database='finance', charset='utf8')
      cursor = mydb.cursor()
      sql = "insert into futureinfo values(%s, %s)"
      cursor.executemany(sql, item) # 要传入一个二维列表或元组的列表作为参数 item,每个子列表或元组应包含两个元素,对应着 SQL 语句中的两个 %s,用于插入数据库表中的两个字段的值
      mydb.commit()
      cursor.close()
      mydb.close()
    • 爬虫操作
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # 获取期货列表:东方财富网今天期货市场中港交所的期货列表信息(??不确定是不是完整期货信息)
      def getInfo(self):
      url_base = 'https://futsseapi.eastmoney.com/list/HKSTOCKF?callback=aaa_callback&xx'
      all_data = list()
      for i in range(30): # 默认30页?
      url = url_base.format(size=20, page=i)
      data_str = requests.get(url).text
      json_str = data_str[data_str.index('(') + 1:-1] # 去除 JSON 数据中的回调函数
      data_list = json.loads(json_str)["list"]
      for item in data_list:
      tmp = [item['dm'], item['name']] # 期货代码,期货名称
      all_data.append(tmp)
      Database().InsertFutureInfo(all_data)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # 获取历史期货数据:通过之前获取的期货列表信息,根据期货代码获取其历史(日线)数据(??不确定完整)
      def getHistory(self):
      url_base = 'https://push2his.eastmoney.com/api/qt/stock/kline/get?secid=130.{secid}&xx'
      all_data = list()
      code_list = Database().GetCodeList() # 获取期货列表信息
      for dm in tqdm(code_list):
      url = url_base.format(secid=dm) # 日线数据
      response = requests.get(url=url, timeout=15)
      klines_data = json.loads(response.text)['data']['klines']
      for data in klines_data:
      res_data = data.split(',')
      res_data = [dm] + res_data # 元组首位加上期货代码
      all_data.append(res_data)
      Database().InsertFutureHistory(all_data)

系统开发

  • 系统架构
    • 前端:Django+Template,Echarts
    • 后端:Django,MySQL,定时任务
  • 主要工作(原)
    • 四大模块:数据更新、数据下载、数据分析、数据维护
      • 一、香港数据
        1. 数据实时更新 (1.1 数据列表(香港股票,香港基金,香港指数,香港期货,香港交易所),1.2 基本信息数据(股票、基金、期货),1.3 基本信息, 1.4 财务报表, 1.5 香港历史交易数据(香港股票,香港基金,香港指数,香港期货), 1.6 当日交易数据(香港股票,香港基金,香港指数,香港期货))
        2. 数据下载 (2.1 列表数据下载(香港股票,香港基金,香港指数,香港期货,香港交易所),2.2 基本信息数据下载(股票、基金、期货),2.3 基本信息下载, 2.4 财务报表数据下载, 2.5 香港历史交易数据下载(大陆股票,大陆基金,大陆指数,大陆期货), 2.6 当日交易数据下载(香港股票,香港基金,香港指数,香港期货))
        3. 数据分析 (3.1 列表数据分析(香港股票,香港基金,香港指数,香港期货,香港交易所),3.2 基本信息数据分析(股票、基金、期货),3.3 基本信息分析, 3.4 财务报表数据分析, 3.5 大陆历史交易数据分析(大陆股票,大陆基金,大陆指数,大陆期货), 3.6 当日交易数据分析(大陆股票,大陆基金,大陆指数,大陆期货))
        4. 数据维护 (4.1 数据导出 4.1.1 数据列表导出(香港股票,香港基金,香港指数,香港期货,香港交易所),4.1.2 基本信息数据导出(股票、基金、期货),4.1.3 基本信息数据导出, 4.1.4 财务报表导出, 4.1.5 香港历史交易数据导出(香港陆股票,香港基金,香港指数,香港期货), 4.1.6 当日交易数据导出(香港股票,香港基金,香港指数,香港期货), 4.2 数据导入 4.2.1 数据列表导入(香港股票,香港基金,香港指数,香港期货,香港交易所),4.2.2 基本信息数据导入(股票、基金、期货),4.2.3 基本信息数据导入, 4.2.4 财务报表导入, 4.2.5 香港历史交易数据导入(香港股票,香港基金,香港指数,香港期货), 4.2.6 当日交易数据导入(香港股票,香港基金,香港指数,香港期货))】
      • 二、香港相关数据还要包括 【1 深港通、沪港通数据;2. 在港上市的大陆股票;3. 在港国企股;4. 港股的科创板】
  • 主要工作(真)
  • 开发流程
    • 港股数据爬取(spider):
      创建各类港股基本信息表:获取“港股市场xx股”(在今日)的信息列表,将“股票代码”、“股票名称”等保存在数据库对应的基本信息表中,同时保存“更新时间”为今日
      创建各类港股历史数据表:根据“港股市场xx股”的基本信息表中的股票代码,获取该股至今的历史数据,将“股票代码, 名称, k线数据, 时间”存入对应的历史数据表中
    • 港股数据更新(spider):
      更新各类港股基本信息表:获取“港股市场xx股”(在今日)的信息列表,将对应表中不存在的“股票代码”的数据存入并且保存“更新时间”为今日,将原本存在的股票信息和“更新时间”更新
      更新各类港股历史数据表:根据“港股市场xx股”的基本信息表中的“股票代码”和“更新时间”,获取从“更新时间”至今的历史数据,存入对应的历史数据表中(无序)
    • 港股基本数据展示(Django):展示各类港股基本信息表
    • 港股K线图展示(Django):根据股票代码,结合Echarts展示各类港股历史数据的K线图
    • 提供数据接口供其他的模块使用(Django):以JSON形式返回各类港股历史数据到页面上
    • 数据下载(Django):以JSON形式返回各类港股历史数据到页面上,右键“另存为”下载
    • 数据维护:导入,导出

数据库建设

  1. 创建数据库和表(\spider\DB_Fund.py)
    • xx股基本信息表 xxinfo
      股票代码 股票名称 最新价 涨跌额 涨跌幅 今开 最高 最低 昨收 成交量(股) 成交额 更新日期
      code name now change changerate open high low yesterday_close volume amount date_updated
    • xx股历史数据表 xxhistory
      code date open now high low volume amount rate changerate per zero
    • A-H股基本信息表 AHStockComparisoninfo
      名称 H股代码 最新价(HKD) 涨跌幅 A股代码 最新价(RMB) 涨跌幅 比价(A/H) 溢价(A/H)% 更新时间
      name code_hkd now_hkd changerate_hkd code_rmb now_rmb changerate_rmb AHcomparison AHcomparisonrate date_updated
    • 港股市场术语英文表示:
      1. 港股主板:HKEXMainBoardinfo
      2. 港股创业板:GEMofHKEXinfo
      3. 知名港股
      4. 蓝筹股
      5. 红筹股
      6. 红筹成分股
      7. 国企股:SOEStocksinfo
      8. 国企成分股:SOEConstituentStocksinfo
      9. 港股通成份股:HKStockConnectConstituentStocksinfo
      10. 人民币交易港股
      11. AH股比价:AHStockComparisoninfo
      12. ADR
      13. 香港指数:HKStockIndexinfo
  2. 爬取港股基本信息(\spider\Get_HKStock\HKStockSpider().getxxInfo())
  3. 爬取港股历史数据(\spider\Get_HKStock\HKStockSpider().getxxHistory())
  4. 数据更新(\spider\Update_HKStock.py)
    • 更新港股基本信息
    • 更新港股历史数据
    • 定时任务
  5. 数据接口(Django实现):以JSON形式返回各类港股历史数据到页面上,供其他的模块使用
  6. 数据下载(Django实现):以JSON形式返回各类港股历史数据到页面上,右键“另存为”保存
  7. 数据维护

数据可视化

  • 项目结构

示例1:展示香港指数基本信息表 HKStockIndexinfo

  1. 编写model(datasite/mainapp/models.py)
    1
    2
    3
    4
    5
    6
    7
    8
    class HKStockIndexinfo(models.Model):
    code = models.CharField(max_length=12, primary_key=True) # 假设code是主键
    name = models.FloatField(max_length=20, db_column='name')
    now = models.FloatField(db_column='now')
    # ...
    class Meta:
    db_table = 'HKStockIndexinfo'
    managed = False
  2. 迁移,将 model与 MySQL中的表映射
    1
    python manage.py makemigrations  # IDE console
  3. 编写html(datasite/mainapp/templates/hkstockindexinfo.html)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <thead>
    <tr>
    <th>代码</th>
    <th>名称</th>
    <!-- ... -->
    </tr>
    </thead>
    <tbody>
    {% for info in infos %}
    <tr>
    <td style="color: #1e8ecc;">{{ info.code }}</td>
    <td style="color: #1e8ecc;">{{ info.name }}</td>
    <!-- ... -->
    </tr>
    {% endfor %}
    </tbody>
  4. 编写应用请求(datasite/mainapp/views.py)
    1
    2
    3
    4
    def hkstockindexinfo(request):
    infos = HKStockIndexinfo.objects.all() # 从MySQL中获取对应表的数据
    context = {'infos': infos} # 创建上下文字典
    return render(request, 'hkstockindexinfo.html', context) # 渲染模板
  5. 配置路由(datasite/datasite/urls.py)
    1
    2
    3
    urlpatterns = [
    path('hkstockindexinfo', views.hkstockindexinfo), # 不需要配置项目-应用两级url
    ]
  6. 启动Django项目
    1
    PS E:\Code\datasite> python manage.py runserver
  7. 发送请求 http://127.0.0.1:8000/hkstockindexinfo, 展示香港指数基本信息表

示例2:使用Echart展示港股主板历史数据k线图

  1. 编写model(datasite/mainapp/models.py)
    1
    2
    3
    4
    5
    6
    7
    8
    class HKEXMainBoardhistory(models.Model):
    code = models.CharField(max_length=12, primary_key=True) # 假设code是主键
    date = models.CharField(max_length=10, db_column='date')
    open = models.FloatField(db_column='open')
    # ...
    class Meta:
    db_table = 'HKEXMainBoardhistory'
    managed = False
  2. 迁移,将 model与 MySQL中的表映射
    1
    python manage.py makemigrations  # IDE console
  3. 编写html(datasite/mainapp/templates/historyklines.html)
    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
    {% load static %}  <!-- 在项目setting.py配置:STATIC_URL = '/static/' -->

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>港股主板历史数据k线图</title>
    <!-- 引入已经下载到本地/static/assets/js下的Echarts文件 -->
    <script src="{% static 'assets/js/echarts.js' %}"></script>
    </head>
    <body>
    <div id="main" style="width: 1600px;height:800px;"></div>
    <script type="text/javascript">
    // 从Echarts官网上复制K线图示例代码
    var chartDom = document.getElementById('main');
    var myChart = echarts.init(chartDom);
    var option;
    // 使用模板语言获取传递的 JSON 数据
    var jsonData = JSON.parse('{{ jsonData | escapejs }}');
    function splitData(rawData) {
    // ...
    }
    function renderItem(params, api) {
    // ...
    }

    // 使用后端查询的数据,代替模版代码中原本外部引入的json文件
    var data = splitData(jsonData);
    myChart.setOption({
    // ...
    });
    </script>
    </body>
    </html>
  4. 编写应用请求(datasite/mainapp/views.py)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def historyklines(request):
    code = request.GET.get('code') # 获取请求中携带的code参数值
    queryset = HKEXMainBoardhistory.objects.filter(code=code) # 查询所有代码为 code 的数据
    jsonData = []
    for item in queryset: # 构造二维数组
    data_row = [
    item.date, item.open, item.now, item.low, item.high, item.volume
    ]
    jsonData.append(data_row)
    jsonData_str = json.dumps(jsonData)
    return render(request, 'historyklines.html', {'jsonData': jsonData_str})
  5. 配置路由(datasite/datasite/urls.py)
    1
    2
    3
    urlpatterns = [
    path('historyklines', views.historyklines),
    ]
  6. 启动Django项目
    1
    PS E:\Code\datasite> python manage.py runserver

示例3:提供数据接口供其他模块调用,下载历史数据

  1. 指的是xx股历史数据
  2. 编写Model、View,配置urls
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def hkexmainboarddata(request):
    code = request.GET.get('code')
    data = HKEXMainBoardhistory.objects.filter(code=code)
    data_dict_list = [{"code": item.code, "date": item.date, "open": item.open, "now": item.now,
    "high": item.high, "low": item.low, "volume": item.volume, "amount": item.amount,
    "rate": item.rate, "changerate": item.changerate, "per": item.per, "zero": item.zero}
    for item in data]
    # 转换为 JSON 格式
    data_json = json.dumps(data_dict_list)
    # 返回 JSON 响应
    return JsonResponse(data_json, safe=False)
  3. 查询页面:输入港股代码,点击“生成K线图”按钮发送xxklines请求/点击“下载历史数据”展示历史数据(右键“另存为”下载)
    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
    <div class="card ">
    <div class="card-header">
    <h4 class="card-title">港股历史数据</h4>
    </div>
    <form id="myForm">
    <label for="codeInput" style="width: 150px;">请输入港股主板代码:</label>
    <input type="text" id="codeInput" name="codeInput" required>
    <button type="submit" id="submitButton">生成K线图</button>
    <button type="submit" id="submitButton0">下载历史数据</button>
    </form>
    </div>
    <script>
    document.getElementById('myForm').addEventListener('submit', function(event) {
    event.preventDefault(); // 阻止表单默认提交行为
    // 获取输入框中的内容
    const code = document.getElementById('codeInput').value;
    if (event.submitter.id === 'submitButton') {
    // 构造生成K线图的请求URL
    const url = `http://127.0.0.1:8000/hkexmainboardklines?code=${code}`;
    // 在新标签页中打开链接
    window.open(url, '_blank');
    } else if (event.submitter.id === 'submitButton0') {
    // 构造下载历史数据的请求URL
    const url = `http://127.0.0.1:8000/hkexmainboarddata?code=${code}`;
    // 在新标签页中打开链接
    window.open(url, '_blank');
    }
    });
    </script>

毕业答辩

毕业答辩的意义是,校方为了进一步的审查论文。
考查内容可归纳如下:①进一步考查和验证毕业论文作者对所着论文论题的认识程度及当场论证论题的能力 ②进一步考查毕业论文作者对专业知识掌握的深度与广度 ③审查毕业论文是否学员自己独立完成
答辩时提问依据:①汇报PPT的内容 ②毕业论文的内容 ③汇报者的言语

答辩安排

答辩日期:5月24日(13周周五),上午1-4节,8:50开始,预计到中午左右结束;
答辩地点:A4206
需携带资料:
1.答辩ppt 电子版(u盘);2.一式3份双面打印的论文(最新版,不需要装帧封面)。
注意事项:
1.请大家提前半小时(上午8:20)到现场,调试好设备(电脑、投影仪、麦克风,等),所有的ppt都拷贝到指定电脑上(可以用课室的电脑,但建议至少带一台笔记本做备用)。
2.提前打印好3份毕设论文(简单的双面打印即可,不需要特别装帧封面,因为不是最终稿),答辩前统一交给老师翻阅。如果在答辩前对论文有修改,和提交系统的版本不一致也没关系,只要打印最新版就可以了。
3.每人答辩陈述时间5分钟左右(可以利用powerpoint的排练功能提前排练好),然后留3分钟左右回答老师的提问。
4.回答问题时要认真记录下老师提出的修改意见,并在答辩结束后按要求修改论文,然后提交论文最终版到维普系统;
5.答辩结束后,老师们会在维普系统上给论文和答辩过程打分,大家把论文最终版提交维普系统之后,可以直接从系统上下载并打印带有老师电子签名的各类表格(一般不需要找老师们手签)。最终版论文一定要装帧好封面,和全套打印的表格一起,按照顺序放入档案袋并交给导师。

答辩内容

各位评委老师,同学们:
大家上午好! 我是网络工程专业的蔡枫,我的论文题目是基于内地与香港金融平台的在线量化投资的数据原型系统的设计与开发
<翻页>
今天我将从以下四个方面阐述我的毕业论文内容。
<翻页>

第一部分 选题背景及意义

随着在线量化投资的普及,如何有效地收集和整理海量的互联网金融数据,成为了一大挑战。
<翻页>
一方面,互联网数据具有实时性,导致用户和金融公司都难以检索过去特定时间的完整金融数据。
另一方面,人们需花费大量时间在门户网站浏览实时金融信息,但缺乏明确直观的数据分析结果。
<翻页>
为有效解决以上问题,本论文设计并开发了基于网络爬虫的数据原型系统,实时获取互联网上的金融数据,并提供数据下载的功能。
另外,系统提供了数据可视化和数据分析的功能,用户能够从中获取具有参考价值的信息。
以及,作为综合性金融系统中的底层数据平台,能够为其他模块的开发提供数据源。
<翻页>

第二部分 开发环境与技术

<翻页>
首先介绍一下网络爬虫,其通过模拟人类浏览网页的行为,获取并解析网络信息。
<翻页>
如果目标网站提供了 API 接口,我们可以通过浏览器的开发者工具进行抓包,并在Python项目中直接调用以获取数据。
<翻页>
系统的开发语言为 Python,采用 Django 框架结合前端技术进行 Web 应用的构建。通过编写 MTV 三层架构代码,开发子系统的各功能。
<翻页>
本系统中选择 MySQL 作为数据库,存储爬取到的网络金融数据,以及进行后续的功能开发。
<翻页>
本系统的数据库中主要有两种表,即各类港股基本信息表和港股历史数据表。
<翻页>
在港股基本信息表中,包括股票代码、股票名称、最新价、涨跌额等基本信息,另外还添加了 date_updated 字段,以便在更新历史数据表时计算出所需更新数据的时间段。
<翻页>
在港股历史数据表中,保存了表示K线数据的相关字段。
<翻页>

第三部分 系统设计与实现

<翻页>
本文的数据原型系统是作为综合性金融系统底层的数据平台模块进行开发的,其他的量化因子分析模块和量化投资策略模块由小组成员负责开发。
<翻页>
在底层的数据平台,即本文的数据原型系统中,保存了香港金融市场股票的交易数据,包括了数据库建设模块、数据可视化模块、数据接口模块、数据分析模块这四个模块。
<翻页>
在数据库建设模块中,主要有三个程序,分别用于创建数据库和表,初始化表数据,以及更新表数据。
其中更新程序是利用 schedule 库,设置定时任务16点实时更新数据到各类港股基本信息表中,根据基本信息表中的的股票代码和更新时间,获取其自更新时间至今的K线数据添加到对应的表中同时修改“更新时间”为当日,以及定期对表执行“转储SQL”操作进行数据备份,以便在需要时快速重构。
<翻页>
在数据可视化模块中,提供了基本信息表格功能。点击子系统导航栏,选择港股类型,在子系统界面上对应的基本信息数据。
<翻页>
实现的基本步骤为:编写模型与数据库表映射,编写视图接收请求和模版页面,并配置好请求访问路径,即可通过浏览器访问系统功能。
<翻页>
另外,提供了历史数据K线图展示功能,根据前端输入的各类港股股票代码,从数据库中获取数据,并利用 ECharts 生成历史数据K线图。
<翻页>
其实现步骤类似前面介绍的基本信息表格。
<翻页>
在数据接口模块中,复用了历史数据K线图功能的功能界面和业务代码,根据输入的各类港股股票代码,从数据库中对应的历史数据表中获取数据,以JSON形式返回到新标签页面上。
<翻页>
数据可以供综合性金融系统中的其他模块使用,用户也可以在页面上右键“另存为”下载数据文件。
<翻页>
最后是数据分析模块:对于采集的金融数据作进一步的分析,呈现更直观和更有价值的信息。我们另外爬取了内地市场沪深京A股的数据,对比分析凸显港陆市场的数据差别。
<翻页>
首先,对于港股与沪深京A股,计算基本统计描述。
<翻页>
可以简单分析看出,香港市场在股价波动性和交易量方面普遍较大,呈现出更高的市场活力和波动性;
内地市场的平均股价虽然较低,但交易量和交易金额却相对较高,显示出更为稳定的交易情况。
<翻页>
然后进一步进行各类数据的可视化分析。我们进行了收盘价分布统计,这是放大后。。
<翻页>
然后是涨跌幅分布比较。。
<翻页>

第四部分 总结与展望

<翻页>
本次毕业设计的工作涵盖了搭建数据原型系统的各个方面,包括金融数据库的设计、系统前端和后端的开发等。
(数据采集:直接从东方财富网接口获取金融数据,并通过数据清洗和整理存储到数据库中。在开发时使用了大模型进行数据含义的分析和编写数据清洗代码,极大地提高了开发效率。对于前端的编写,同组的同学提供了模板,为开发带来了很大的便利。只需开发子系统功能界面,将模板页面中空缺内容区块覆盖。在系统后端的开发过程中,我快速学习了Django的开发流程,进行需求分析和功能设计后,解决实际开发问题,同时总结经验完善系统。)
同时,对于这几个方面,未来还有需要改进的地方。
<翻页>
对于这几个方面,未来也有需要改进的地方,如加强数据采集时对异常情况的处理,优化系统前后端性能等。。
(在数据采集方面,引入多线程实现对数据的并行处理,另外还需加强对异常情况的处理,确保系统稳健运行。在系统前端方面,进一步完善用户界面的设计和用户体验,考虑引入前端框架如React、Vue.js等,以提升前端开发的效率和灵活性,提供更加直观和友好的数据展示方式。在系统后端方面,不断优化系统架构和性能,加强对系统的监控和管理,及时发现和解决潜在的问题,确保系统长期稳定运行。)
<翻页>
最后感谢各位老师的聆听及指导,我的汇报到此结束。

问答环节

  1. 为什么会选择这个课题?
    (自身原因+外部因素=选择的原因,把选择的原因放大,从多角度回答)
    我认为计算机金融有潜力,以及量化投资的研究在当下是非常有价值的,有很强的现实意义,其中数据平台的构建是基石。
    本次毕业设计的工作涵盖了搭建数据原型系统的各个方面,包括金融数据库的设计、系统前端和后端的搭建等,提供一种快速构建的思路。
  2. 论文的创新点体现在哪里?
    (通过和已有成果或内容相比较,从内容和研究方法上来讲创新。)
    回答参考:本篇毕业论文的创新之处在于讨论 xxx 的问题时不仅在 xxx 方面列出xxx,也从实际的角度进行了举例论证。一是在方法上,本文将Xx方法与XX方法相结合,能够有效消除单一方法带来的误差,有效提高了数据的精度(结果的有效性)。二是在理论上,综合近十年的文献发现,目前还没有研究学者提出 xXX方面的内容,本文在理论的提出上是一种全新的尝试
  3. 本论文中你主要做了什么?
    查找了大量的文献、书籍报刊,对这些资料进行了精谀,结合国内外的研究现状,对 xxx问题进行了归纳总结;在此基础上,提炼出本课题的核心,对xxx展开研究,并运用 xxx 方法进行研究;对xxx 研究结果进行分析,提出 solvexxx 问题的建议及措施。
  4. 本论文的意义和目的是什么?
    (理论意义+实践意义)在理论方面,本文应用了 Xxx的专业方法,对于丰富果,并以具体的 xxx 例子展开应用得到较好的应用结果。
  5. 写论文的过程中有哪些困难,怎么克服的?
    (如实叙即可,但一定要说问题是 solve 的!)
    在撰写论文的过程中在xx方面(资料收集问卷发放实验开展)遇到有关 xxx(找不到相应主题的学术文献)困难,在与指导老师沟通后,查阅了老师推。荐的相关书籍、资料以及文献信息,也请教了直系的学长学姐们,最后 solve 了相关问题,在此也非常感谢给我提供帮助的他们。
  6. 论文哪些地方可以继续改进?
    目前对于这个课题的认识确实还不够具体,所以在xxx 方萄的研究片法有一定限度,研究成果与前辈们的研究成果也确实相似,没有做到能够更加深入的研究。同时也受限于条件没能更多的试验方法
  7. 论文中的数据和资料从哪里来?
    我的资料主要来自于知网等学术网站和导师提供的资料文献,数据是通过调查法以及些同类课题的数据参考,再统一分析计算得出
  8. 论文中相关定义的解释,理论解释?
    要求你掌握本文研究中重点的一些概念及定义。有一些概率可以做一些生活中的举例来加深记忆。回答的时候也可以根据定义来加以拓展,比如:老师,这个问题我是这样理解的,以生活中的案例来举例 xxx 概念就好。比 XXX中的XXXX,对xxx是非常重要的。

RabbitMQ

一、初识MQ

1. 同步和异步通讯

微服务间通讯有同步和异步两种方式:
1、同步通讯:就像打电话,需要实时响应;
2、异步通讯:就像发邮件,不需要马上回复。
两种方式各有优劣,打电话可以立即得到响应,但是你却不能跟多个人同时通话。发送邮件可以同时与多个人收发邮件,但是往往响应会有延迟。

  1. 同步通讯
    我们之前学习的Feign调用就属于同步方式,虽然调用可以实时得到结果,但存在下面的问题:
    同步调用的优点:

    • 时效性较强
    • 可以立即得到结果

    同步调用的问题:

    • 耦合度高
    • 性能和吞吐能力下降
    • 有额外的资源消耗
    • 有级联失败问题
  2. 异步通讯:常见实现是事件驱动模式
    异步调用则可以避免上述问题:我们以购买商品为例,用户支付后需要调用订单服务完成订单状态修改,调用物流服务,从仓库分配响应的库存并准备发货。
    在事件模式中,支付服务是事件发布者(publisher),在支付完成后只需要发布一个支付成功的事件(event),事件中带上订单id。订单服务和物流服务是事件订阅者(Consumer),订阅支付成功的事件,监听到事件后完成自己业务即可。为了解除事件发布者与订阅者之间的耦合,两者并不是直接通信,而是有一个中间人(Broker)。发布者发布事件到Broker,不关心谁来订阅事件。订阅者从Broker订阅事件,不关心谁发来的消息。
    Broker 是一个像数据总线一样的东西,所有的服务要接收数据和发送数据都发到这个总线上,这个总线就像协议一样,让服务间的通讯变得标准和可控。

    好处:

    • 吞吐量提升:无需等待订阅者处理完成,响应更快速
    • 故障隔离:服务没有直接调用,不存在级联失败问题
    • 调用间没有阻塞,不会造成无效的资源占用
    • 耦合度极低,每个服务都可以灵活插拔,可替换
    • 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件

    缺点:

    • 架构复杂了,业务没有明显的流程线,不好管理;
    • 需要依赖于Broker的可靠、安全、性能
      好在现在开源软件或云平台上 Broker 的软件是非常成熟的,比较常见的一种就是我们今天要学习的MQ技术。

2. 技术对比

MQ,中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。
比较常见的MQ实现:ActiveMQ,RabbitMQ,RocketMQ,Kafka。几种常见MQ的对比:

RabbitMQ ActiveMQ RocketMQ Kafka
公司/社区 Rabbit Apache 阿里 Apache
开发语言 Erlang Java Java Scala&Java
协议支持 AMQP,XMPP,SMTP,STOMP OpenWire,STOMP,REST,XMPP,AMQP 自定义协议 自定义协议
可用性 一般
单机吞吐量 一般 非常高
消息延迟 微秒级 毫秒级 毫秒级 毫秒以内
消息可靠性 一般 一般

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka

追求消息低延迟:RabbitMQ、Kafka

二、快速入门

1. 安装RabbitMQ

MQ的基本结构:
RabbitMQ中的一些角色:

  • publisher:生产者
  • consumer:消费者
  • exchange:交换机,负责消息路由
  • queue:队列,存储消息
  • virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离

2. RabbitMQ消息模型

3. 导入Demo工程


包括三部分:

  • mq-demo:父工程,管理项目依赖
  • publisher:消息的发送者
  • consumer:消息的消费者

4. 入门案例

简单队列模式的模型图:
官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:

  • publisher:消息发布者,将消息发送到队列queue
  • queue:消息队列,负责接受并缓存消息
  • consumer:订阅队列,处理队列中的消息
  1. publisher实现
    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
    public class PublisherTest {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
    // 1.建立连接
    ConnectionFactory factory = new ConnectionFactory();
    // 1.1.设置连接参数,分别是:主机名(改成自己的ip)、端口号、vhost、用户名、密码
    factory.setHost("192.168.150.101");
    factory.setPort(5672);
    factory.setVirtualHost("/");
    factory.setUsername("itcast");
    factory.setPassword("123321");
    // 1.2.建立连接
    Connection connection = factory.newConnection();

    // 2.创建通道Channel
    Channel channel = connection.createChannel();

    // 3.创建队列
    String queueName = "simple.queue";
    channel.queueDeclare(queueName, false, false, false, null);

    // 4.发送消息
    String message = "hello, rabbitmq!";
    channel.basicPublish("", queueName, null, message.getBytes());
    System.out.println("发送消息成功:【" + message + "】");

    // 5.关闭通道和连接
    channel.close();
    connection.close();
    }
    }
  2. consumer实现
    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
    public class ConsumerTest {
    public static void main(String[] args) throws IOException, TimeoutException {
    // 1.建立连接
    ConnectionFactory factory = new ConnectionFactory();
    // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
    factory.setHost("192.168.150.101");
    factory.setPort(5672);
    factory.setVirtualHost("/");
    factory.setUsername("itcast");
    factory.setPassword("123321");
    // 1.2.建立连接
    Connection connection = factory.newConnection();

    // 2.创建通道Channel
    Channel channel = connection.createChannel();

    // 3.创建队列
    String queueName = "simple.queue";
    channel.queueDeclare(queueName, false, false, false, null);

    // 4.订阅消息
    channel.basicConsume(queueName, true, new DefaultConsumer(channel){
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope,
    AMQP.BasicProperties properties, byte[] body) throws IOException {
    // 5.处理消息
    String message = new String(body);
    System.out.println("接收到消息:【" + message + "】");
    }
    });
    System.out.println("等待接收消息。。。。");
    }
    }
    消息被消费后,就消失(阅后即焚)

三、SpringAMQP

SpringAMQP 是基于 RabbitMQ 封装的一套模板,并且还利用 SpringBoot 对其实现了自动装配,使用起来非常方便。
SpringAMQP提供了三个功能:

  • 自动声明队列、交换机及其绑定关系
  • 基于注解的监听器模式,异步接收消息
  • 封装了RabbitTemplate工具,用于发送消息

1. Basic Queue 简单队列模型

在父工程mq-demo中引入依赖

1
2
3
4
5
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. 消息发送
    首先配置MQ地址,在publisher服务的application.yml中添加配置:
    1
    2
    3
    4
    5
    6
    7
    spring:
    rabbitmq:
    host: 192.168.150.101 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码
    然后在publisher服务中编写测试类SpringAmqpTest,并利用RabbitTemplate实现消息发送:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringAmqpTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void testSimpleQueue() {
    // 队列名称
    String queueName = "simple.queue";
    // 消息
    String message = "hello, spring amqp!";
    // 发送消息
    rabbitTemplate.convertAndSend(queueName, message);
    }
    }
  2. 消息接收
    首先配置MQ地址,在consumer服务的application.yml中添加配置:
    1
    2
    3
    4
    5
    6
    7
    spring:
    rabbitmq:
    host: 192.168.150.101 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码
    然后在consumer服务的cn.itcast.mq.listener包中新建一个类SpringRabbitListener,代码如下:
    1
    2
    3
    4
    5
    6
    7
    @Component
    public class SpringRabbitListener {
    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueueMessage(String msg) throws InterruptedException {
    System.out.println("spring 消费者接收到消息:【" + msg + "】");
    }
    }

2. WorkQueue

Work queues,也被称为(Task queues),任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息
当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用 work 模型,多个消费者共同处理消息处理,速度就能大大提高了。

  1. 消息发送:在publisher服务中的SpringAmqpTest类中添加一个测试方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * workQueue
    * 向队列中不停发送消息,模拟消息堆积。
    */
    @Test
    public void testWorkQueue() throws InterruptedException {
    // 队列名称
    String queueName = "simple.queue";
    // 消息
    String message = "hello, message_";
    for (int i = 0; i < 50; i++) {
    // 发送消息
    rabbitTemplate.convertAndSend(queueName, message + i);
    Thread.sleep(20);
    }
    }
  2. 消息接收:要模拟多个消费者绑定同一个队列,我们在consumer服务的SpringRabbitListener中添加2个新的方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @RabbitListener(queues = "simple.queue")
    public void listenWorkQueue1(String msg) throws InterruptedException {
    System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(20);``
    }
    @RabbitListener(queues = "simple.queue")
    public void listenWorkQueue2(String msg) throws InterruptedException {
    System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(200);
    }
  3. 测试:启动ConsumerApplication后,在执行publisher服务中刚刚编写的发送测试方法testWorkQueue。
    可以看到消费者1很快完成了自己的25条消息。消费者2却在缓慢的处理自己的25条消息。也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。这样显然是有问题的。
    在spring中有一个简单的配置,可以解决这个问题。我们修改consumer服务的application.yml文件,添加配置:
    1
    2
    3
    4
    5
    spring:
    rabbitmq:
    listener:
    simple:
    prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

3. 发布/订阅

发布订阅的模型如图:
可以看到,在订阅模型中,多了一个exchange角色,而且过程略有变化:

  • Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
  • Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有以下3种类型:
    • Fanout:广播,将消息交给所有绑定到交换机的队列
    • Direct:定向,把消息交给符合指定routing key 的队列
    • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
  • Consumer:消费者,与以前一样,订阅队列,没有变化
  • Queue:消息队列也与以前一样,接收消息、缓存消息。

Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
这样,实现了一个消息发送给多个消费者。

4. Fanout

在广播模式下,消息发送流程是这样的:

  • 1) 可以有多个队列
  • 2) 每个队列都要绑定到Exchange(交换机)
  • 3) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定
  • 4) 交换机把消息发送给绑定过的所有队列
  • 5) 订阅队列的消费者都能拿到消息

我们的计划是这样的:

  • 创建一个交换机 itcast.fanout,类型是Fanout
  • 创建两个队列fanout.queue1和fanout.queue2,绑定到交换机itcast.fanout
  1. 声明队列和交换机
    Spring提供了一个接口Exchange,来表示所有不同类型的交换机:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 在consumer中创建一个类,声明队列和交换机
    @Configuration
    public class FanoutConfig {
    /**
    * 声明交换机
    * @return Fanout类型交换机
    */
    @Bean
    public FanoutExchange fanoutExchange(){ return new FanoutExchange("itcast.fanout"); }
    // 队列1,绑定交换机
    @Bean
    public Queue fanoutQueue1(){ return new Queue("fanout.queue1"); }
    @Bean
    public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
    return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }
    // 队列2,绑定交换机
    @Bean
    public Queue fanoutQueue2(){ return new Queue("fanout.queue2"); }
    @Bean
    public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
    return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
    }
  2. 消息发送
    1
    2
    3
    4
    5
    6
    7
    8
    // 在publisher服务的SpringAmqpTest类中添加测试方法
    @Test
    public void testFanoutExchange() {
    String exchangeName = "itcast.fanout";
    String message = "hello, everyone!";
    // 发送到交换机
    rabbitTemplate.convertAndSend(exchangeName, "", message);
    }
  3. 消息接收
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 在consumer服务的SpringRabbitListener中添加两个方法,作为消费者
    @RabbitListener(queues = "fanout.queue1")
    public void listenFanoutQueue1(String msg) {
    System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
    }
    @RabbitListener(queues = "fanout.queue2")
    public void listenFanoutQueue2(String msg) {
    System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
    }

5. Direct

在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
在Direct模型下:

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个BindingKey(路由key)
  • 消息的发送方在 向 Exchange发送消息时,必须指定消息的 RoutingKey
  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的RoutingKey进行判断,只有队列的Bindingkey与消息的 Routingkey完全一致,才会接收到消息
  • 一个队列可以有多个BindingKey。如果多个队列具有相同的RoutingKey,则与Fanout功能类似(可以模拟广播)
  1. 基于注解声明队列和交换机
    基于@Bean的方式声明队列和交换机比较麻烦,Spring还提供了基于注解方式来声明。
    在consumer的SpringRabbitListener中添加两个消费者,同时基于注解 @RabbitListener 来声明队列和交换机:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 消费者1+队列+交换机+BindingKey
    @RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "direct.queue1"),
    exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
    key = {"red", "blue"}
    ))
    public void listenDirectQueue1(String msg){
    System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
    }
    // 消费者2+队列+交换机+BindingKey
    @RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "direct.queue2"),
    exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
    key = {"red", "yellow"}
    ))
    public void listenDirectQueue2(String msg){
    System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
    }
  2. 消息发送
    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testSendDirectExchange() {
    String exchangeName = "itcast.direct";
    String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
    // 发送消息,携带 RoutingKey
    rabbitTemplate.convertAndSend(exchangeName, "red", message);
    }

6.Topic

Topic类型的ExchangeDirect相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!
Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
通配符规则:#:匹配一个或多个词;*:匹配不多不少恰好1个词
举例:item.#:能够匹配item.spu.insert 或者 item.spuitem.*:只能匹配item.spu

解释:

  • Queue1:绑定的是china.# ,因此凡是以 china.开头的routing key 都会被匹配到。包括china.news和china.weather
  • Queue2:绑定的是#.news ,因此凡是以 .news结尾的 routing key 都会被匹配。包括china.news和japan.news
  1. 消息发送
    1
    rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
  2. 消息接收
    1
    2
    3
    4
    5
    @RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "topic.queue1"),
    exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
    key = "china.#"
    ))
  3. 总结
    描述下Direct交换机与Topic交换机的差异?
    • Topic交换机接收的消息RoutingKey必须是多个单词,以 . 分割
    • Topic交换机与队列绑定时的bindingKey可以指定通配符
    • #:代表0个或多个词
    • *:代表1个词

7.消息转换器

之前说过,Spring会把你发送的消息序列化为字节发送给MQ,接收消息的时候,还会把字节反序列化为Java对象。

image-20200525170410401

只不过,默认情况下Spring采用的序列化方式是JDK序列化。众所周知,JDK序列化存在下列问题:

  • 数据体积过大
  • 有安全漏洞
  • 可读性差

显然,JDK序列化方式并不合适。我们希望消息体的体积更小、可读性更高,因此可以使用JSON方式来做序列化和反序列化。

在publisher和consumer两个服务中都引入依赖:

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>

配置消息转换器。在启动类中添加一个Bean即可:

1
2
3
4
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}

SpringCloud

单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。优点:架构简单、部署成本低。缺点:耦合度高,扩展性差,适合小型项目。例如:学生管理系统
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝
分布式架构的要考虑的问题有:服务拆分粒度如何?服务集群地址如何维护?服务之间如何实现远程调用?服务健康状态如何感知?

微服务:一种良好的分布式架构方案。

  • 优点:拆分粒度更小、服务更独立、耦合度更低。缺点:架构非常复杂,运维、监控、部署难度提高
  • 微服务特点:
    • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发;每个服务单元都可以独立开发、部署、扩展和管理。这些服务单元相互协作,通过网络进行通信,通常使用轻量级的通信机制(如HTTP或消息队列)进行交互。使得应用程序更具弹性、可扩展性和灵活性,同时也有利于团队之间的独立开发和部署。
    • 面向服务:微服务对外暴露业务接口,供其他微服务使用。
    • 自治:团队独立、技术独立、数据独立、部署独立,不同微服务都应该有自己的数据库。
    • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题。 再加上服务异常定位、节点状态监控、自动化部署

微服务这种方案需要技术框架来落地,全球的互联网公司都在积极尝试自己的微服务落地技术。在国内最知名的就是SpringCloud和阿里巴巴的Dubbo(升级SpringCloudAlibaba(实现了SpringCloud接口,是SpringCloud的一部分))微服务技术对比:

企业中常见的四种需求:


Spring Cloud

SpringCloud 是一个基于 Spring Boot 的开源框架,旨在简化构建分布式系统中的微服务架构。它提供了一系列工具和库,用于实现微服务架构中常见的模式和功能,如服务发现、负载均衡、配置管理、断路器模式、消息总线等。
SpringCloud 集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验:
对于 SpringBoot 的版本要求

  • 服务拆分
    • 微服务需要根据业务模块拆分,做到单一职责,避免重复业务开发;
    • 微服务可以将业务暴露为接口,供其他微服务使用。
    • 不同微服务都应该有自己的数据库。
  • 项目结构
    • 两种工程结构
      • 独立 Project:??
      • Maven 聚合:每个微服务作为 主Project 中的一个模块 Module,分别独立打包部署运行,只不过代码放在一个 Project
    • cloud-demo:父工程,管理依赖
      • order-service:订单微服务,负责订单相关业务
      • user-service:用户微服务,负责用户相关业务
      • 在每个模块的 application.yaml 配置该微服务名字、端口号、数据库、日志位置、、
      • 要求:订单微服务和用户微服务都必须有各自的数据库,相互独立;订单服务和用户服务都对外暴露Restful的接口;订单服务如果需要查询用户信息,只能调用用户服务的Restful接口,不能查询用户数据库
  • 远程调用
    • 物理上两个微服务隔离,无法查询各自数据库,但网络上微服务相通
    • 在一个微服务中,通过java代码发送 http 请求,使用另一个微服务的接口以获取其数据库中的数据
    • 在一个微服务的启动类(本身也是配置类)中注册 RestTemplate,配置成 Bean;
    • 在 service 中注入 restTemplate(建议使用构造函数),通过 getForObject(“url”) 发送http请求,将返回的 json 数据转换为制定对象类型

Eureka 服务注册中心(白雪)

  • 在 Eureka 架构中,微服务角色有两类:
    • EurekaServer:服务端,注册中心。记录服务信息、心跳监控
    • EurekaClient:客户端
      • Provider:服务提供者,例如案例中的 user-service,注册自己的信息到 EurekaServer,每隔30秒向EurekaServer发送心跳
      • consumer:服务消费者,例如案例中的 order-service,根据服务名称从 EurekaServer 拉取服务列表,基于服务列表做负载均衡,选中一个微服务后发起远程调用
  • Eureka的作用
    • 消费者该如何获取服务提供者具体信息? 服务提供者启动时向 eureka 注册自己的信息,eureka 保存这些信息,消费者根据服务名称向 eureka 拉取提供者信息
    • 如果有多个服务提供者,消费者该如何选择? 服务消费者利用负载均衡算法,从服务列表中挑选一个
    • 消费者如何感知服务提供者健康状态? 服务提供者会每隔30秒向 EurekaServer 发送心跳请求,报告健康状态 eureka 会更新记录服务列表信息,心跳不正常会被剔除,消费者就可以拉取到最新的信息
  • Eureka 服务搭建:Eruka 自己就是一个微服务。新建项目,引入 eureka-server 依赖;编写启动类,添加 @EnableEurekaServer 注解;添加 application.yml,添加配置
  • Eureka 服务注册:把一个微服务注册到 EurekaServer。
    1. 微服务项目引入 eureka-client 依赖,
    2. 在 application.yml 编写依赖:name、url…
  • Eureka 服务发现:
    服务拉取是(服务A)基于(服务B)服务名称获取(服务B多个实例)服务列表,然后在对服务列表做负载均衡
    1. 修改 orderService(服务B)的代码,修改访问的 userService 的url路径,用服务名代替ip、端口,消除硬编码:
      1
      String url ="http://userservice/user/" + order.getUserId();
    2. 在 order-service 项目(服务B)的启动类 OrderApplication中 的 RestTemplate 添加负载均衡注解
      1
      2
      3
      4
      5
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
      return new RestTemplate();
      }

Ribbon 负载均衡(白雪)

  • 负载均衡原理
    • 加了 @LoadBalanced 注解的RestTemplate,发送的请求将会被 Ribbon 拦截(SpringCloudRibbon的底层采用了一个拦截器),对地址做了修改,处理后发送 HTTP 请求
      基本流程如下:
      • 拦截我们的RestTemplate请求http://userservice/user/1
      • RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
      • DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
      • eureka返回列表,localhost:8081、localhost:8082
      • IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
      • RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1, 发起真实请求
  • 负载均衡策略IRule
    • IRule 接口:决定了负载均衡的策略,每一个子接口都是一种规则,轮询、随机、、、 通过定义 IRule 实现可以修改负载均衡策略
    • 负载均衡策略:负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:
      内置负载均衡规则类 规则描述
      RoundRobinRule 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
      AvailabilityFilteringRule 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。
      WeightedResponseTimeRule 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
      ZoneAvoidanceRule【默认】 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询
      BestAvailableRule 忽略那些短路的服务器,并选择并发数较低的服务器。
      RandomRule 随机选择一个可用的服务器。
      RetryRule 重试机制的选择逻辑
  • 自定义负载均衡策略
    1. 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
      1
      2
      3
      4
      @Bean
      public IRule randomRule(){
      return new RandomRule();
      }
    2. 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
      1
      2
      3
      userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
      ribbon:
      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
    3. 注意,一般用默认的负载均衡规则,不做修改。
  • 饥饿加载
    • Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
      1
      2
      3
      4
      ribbon:
      eager-load:
      enabled: true
      clients: userservice

Nacos注册中心【8848】

  • Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富,在国内受欢迎程度较高。
  • Nacos服务搭建:下载安装包,解压,在 D:\nacos-server-1.4.1\bin 目录下cmd运行指令: startup.cmd -m standalone
    浏览器打开 http://xxxx:8848/nacos/index.html#/login,输入账号/密码:nacos

1. Nacos服务注册或发现

父工程添加 spring-cloud-alibaba 依赖;微服务项目引入 nacos.discovery 依赖,yml 配置 nacos 地址 spring.cloudnacos.server-addr
Nacos 是 SpringCloudAlibaba 的组件,而 SpringCloudAlibaba 也遵循 SpringCloud 中定义的服务注册、服务发现规范。因此使用 Nacos 和使用 Eureka 对于微服务来说,并没有太大区别。

  1. 引入依赖:
    在cloud-demo父工程的pom文件中的<dependencyManagement>中引入SpringCloudAlibaba的依赖;
    1
    2
    3
    4
    5
    6
    7
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    然后在子微服务 user-service 和 order-service 中的pom文件中引入nacos-discovery依赖
    1
    2
    3
    4
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  2. 配置nacos地址:在子微服务 user-service 和 order-service 的 application.yml 中添加 nacos 地址:
    1
    2
    3
    4
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
  3. 重启微服务后,登录nacos管理页面http://10.195.138.48:8848/nacos/index.html#/ ,可以看到微服务信息
  4. 服务发现:消费者需要连接nacos以拉取和订阅服务,因此服务发现的前两步与服务注册是一样(引入nacos-discovery依赖和配置nacos地址),后面再加上服务调用即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private final DiscoveryClient discoveryclient;
    private void handlecartItems(List<CartVO> vos) {
    // 1.根据服务名称,拉取服务的实例列表
    List<ServiceInstance> instances = discoveryclient.getInstances("item-service");
    // 2.负藏均衡,随机挑选一个实例
    ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
    // 3.获取实例的TP和端口
    URI uri= instance.getUri();
    // ...
    }

2. Nacos服务分级存储

一个服务可以有多个实例,例如我们的user-service,可以有:127.0.0.1:8081,127.0.0.1:8082,127.0.0.1:8083
假如这些实例分布于全国各地的不同机房,例如:127.0.0.1:8081在上海机房,127.0.0.1:8082在上海机房,127.0.0.1:8083在杭州机房。 Nacos就将同一机房内的实例 划分为一个集群

  • 也就是说,user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型:
    微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群:
  • 配置集群:修改user-service的application.yml文件,添加集群配置:
    1
    2
    3
    4
    5
    6
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: HZ # 集群名称
  • 同集群优先的负载均衡:默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。因此Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。
    修改order-service的application.yml文件,修改负载均衡规则:
    1
    2
    3
    userservice:
    ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则

3. 权重配置:

默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
在nacos控制台,找到user-service的实例列表,点击编辑,在弹出的窗口修改权重:(注意:权重为0,则该实例永远不会被访问)

4. 环境隔离

Nacos提供了namespace来实现环境隔离功能(比如把一些功能紧密的服务隔离在一起,不能被另一些服务访问)

  • nacos中可以有多个namespace
  • namespace下可以有group、service等
  • 不同namespace之间相互隔离,例如不同namespace的服务互相不可见
  1. 创建namespace:默认情况下,所有service、data、group都在同一个namespace,名为public: 我们可以点击页面新增按钮,添加一个namespace;然后,填写表单,创建一个新的namespace:
  2. 给微服务配置namespace:只能通过修改配置来实现。例如,修改order-service的application.yml文件:
    1
    2
    3
    4
    5
    6
    7
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: HZ
    namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
    重启order-service后,访问控制台,可以看到下面的结果: 此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错:

5. Nacos与Eureka的区别

Nacos和Eureka整体结构类似,服务注册、服务拉取、心跳等待,但是也存在一些差异:

  • Nacos与eureka的共同点
    • 都支持服务注册和服务拉取
    • 都支持服务提供者心跳方式做健康检测
  • Nacos与Eureka的区别
    • Nacos的服务实例分为两种类型:
      • 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
      • 非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
        配置一个服务实例为永久实例:
        1
        2
        3
        4
        cloud:
        nacos:
        discovery:
        ephemeral: false # 设置为非临时实例
    • Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
    • 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
    • Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
    • Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式

6. Nacos配置管理

Nacos除了可以做注册中心,同样可以做配置管理来使用。

  • 统一配置管理
    当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理所有实例的配置。
    Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。(不用重启服务就能更新)
    1. 在nacos中添加配置文件

      注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。

    2. 从微服务拉取配置:微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动。 但如果尚未读取application.yml,又如何得知nacos地址呢?
      因此spring引入了一种新的配置文件:bootstrap.yaml文件,会在application.yml之前被读取,流程如下:
      首先,在user-service服务中,引入nacos-config的客户端依赖
      1
      2
      3
      4
      5
      <!--nacos配置管理依赖-->
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
      </dependency>
      然后,在user-service中添加一个bootstrap.yaml文件,内容如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      spring:
      application:
      name: userservice # 服务名称
      profiles:
      active: dev #开发环境,这里是dev
      cloud:
      nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
      file-extension: yaml # 文件后缀名
      这里会根据spring.cloud.nacos.server-addr获取nacos地址,再根据
      ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id,来读取配置。本例中,就是去读取userservice-dev.yaml
      读取nacos配置:在user-service中的UserController中添加业务逻辑,读取pattern.dateformat配置
  • 配置热更新
    我们最终的目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新。可以使用两种方式:
    1. 方式一:在 @Value 注入的变量所在类上添加注解 @RefreshScope:
    2. 方式二:使用 @ConfigurationProperties 注解代替 @Value 注解。
      在 user-service 服务中,添加一个类,读取 patterrn.dateformat 属性:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      package cn.itcast.user.config;
      import lombok.Data;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.stereotype.Component;

      @Component
      @Data
      @ConfigurationProperties(prefix = "pattern")
      public class PatternProperties {
      private String dateformat;
      }
      在 UserController 中使用这个类代替 @Value:
  • 配置共享
    其实微服务启动时,会去nacos读取多个配置文件,例如:
    • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
    • [spring.application.name].yaml,例如:userservice.yaml
      [spring.application.name].yaml不包含环境,因此可以被多个环境共享。下面我们通过案例来测试配置共享:
    1. 添加一个环境共享配置:我们在nacos中添加一个userservice.yaml文件:
    2. 在user-service中读取共享配置
    3. 运行两个UserApplication,使用不同的profile
      ??
    4. 配置共享的优先级
      当nacos、服务本地同时出现相同属性时,优先级有高低之分:
  • 搭建Nacos集群
    企业生产中强调“高可用”,Nacos 生产环境下一定要部署为集群状态,部署方式参考课前资料中的文档:nacos集群搭建.md

Feign 远程调用

Feign是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送;
解决从前利用 RestTemplate 发起远程调用代码中的问题:1、代码可读性差,编程体验不统一;2、参数复杂URL难以维护
OpenFeign是一个声明式的http客户端,是SpringCloud在Eureka公司开源的Feign基础上改造而来。其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送.

1. Feign替代RestTemplate

  1. 引入依赖:在order-service服务的pom文件中引入feign的依赖:
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    1
    2
    3
    4
    5
    <!-- 负载均衡?? -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalance</artifactId>
    </dependency>
  2. 添加注解:在order-service的启动类添加 @EnableFeignClients注解 开启Feign的功能:
  3. 编写Feign的客户端:在order-service中新建一个接口,添加 @FeignClient注解,内容如下:
    1
    2
    3
    4
    5
    @FeignClient("userservice")
    public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
    }
    这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:服务名称:userservice,请求方式:GET,请求路径:/user/{id},请求参数:Long id,返回值类型:User。这样,当服务调用者需要调用服务提供者时,只需要通过Feign客户端调用接口方法即可,Feign会根据注解信息自动发起HTTP请求并处理响应,而无需使用 RestTemplate。
    Feign的client是写在服务调用者模块中,用于定义如何调用服务提供者的接口,而服务提供者模块则负责提供具体的业务逻辑和处理请求。
  4. 测试:修改order-service中的OrderService类中的queryOrderById方法,使用Feign客户端代替RestTemplate:

2. 日志??

Feign可以支持很多的自定义配置,如下表所示:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign. Contract 支持的注解格式 默认是SpringMVC的注?
feign. Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
自定义配置的两种方法:

  1. 配置文件方式:基于配置文件修改feign的日志级别可以针对单个服务,也可以针对所有服务:
  2. Java代码方式:也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象
    如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中;如果是局部生效,则把它放到对应的@FeignClient这个注解中

3. Feign使用优化

Feign 是一个声明式客户端,只是把声明变成 http 请求,底层还是发http请求,依赖于其它的框架。
其底层客户端实现包括 URLConnection:默认实现,不支持连接池;Apache HttpClient:支持连接池;OKHttp:支持连接池。因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。用Apache的HttpClient来演示:

  • 引入依赖:在order-service的pom文件中引入Apache的HttpClient依赖:
    1
    2
    3
    4
    5
    <!--httpClient的依赖 -->
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    </dependency>
  • 配置连接池:在order-service的application.yml中添加配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    feign:
    client:
    config:
    default: # default全局的配置
    loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
    httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

4. 最佳实践

所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式。
如果一个微服务需要重复被其他微服务调用(消费),那么每个消费者都要重复编写一个 client。有没有办法简化这种重复的代码编写呢?

  1. 从一个微服务中拆出一个模块,将Feign的Client、接口有关的POJO、默认的Feign配置都放到这个模块中,供给所有消费者使用
    例如,将UserClient、User、Feign的默认配置都抽取到一个feign-api包中,所有其他微服务需要消费该微服务时,引用该包的依赖,即可直接使用。
  2. 将所有的微服务中的 dto、client、config 统一放在一个模块中,供给所有消费者使用。
    junbo-api也是如此??!!

Gateway 服务网关

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

1. 为什么需要网关

Gateway网关是我们服务的守门神,所有微服务的统一入口。前端只需要知道网关的端口,发送请求就行了。
架构图:

网关的核心功能特性

  • 权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
  • 路由和负载均衡:一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
  • 限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

在SpringCloud中网关的实现包括两种:gateway、zuul

  • Zuul是基于Servlet的实现,属于阻塞式编程。
  • SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

2. gateway快速入门

  1. 创建 SpringBoot 工程 gateway 服务,引入网关依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--网关-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--nacos服务发现依赖-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  2. 编写启动类
    1
    2
    3
    4
    5
    6
    @SpringBootApplication
    public class GatewayApplication {
    public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
    }
    }
  3. 编写基础配置和路由规则:
    创建application.yml文件,内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    server:
    port: 10010 # 网关端口
    spring:
    application:
    name: gateway # 服务名称
    cloud:
    nacos:
    server-addr: localhost:8848 # nacos地址
    gateway:
    routes: # 网关路由配置
    - id: user-service # 1. 路由id,自定义,只要唯一即可
    # uri: http://127.0.. # 2. 路由的目标地址 http 就是固定地址
    uri: lb://userservice # lb(loadbalance) 就是负载均衡,后面跟服务名称
    predicates: # 3. 路由断言(可多条),判断请求是否符合路由规则的条件
    - Path=/user/** # 按照路径匹配,只要以/user/开头就符合要求
    filters: # 4. 路由过滤器,,
    - id: order-service # ...
    我们将符合Path规则的一切请求,都代理到uri参数指定的地址。本例中,我们将 /user/**开头的请求,代理到lb://userservice,lb是负载均衡,根据服务名拉取服务列表,实现负载均衡。
  4. 网关路由的流程图

3. 断言工厂

我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
例如Path=/user/**是按照路径匹配,这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的,像这样的断言工厂在SpringCloudGateway还有十几个:

名称 说明 示例
After 是某个时间点后的请求 - After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before 是某个时间点之前的请求 - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between 是某两个时间点之前的请求 - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie 请求必须包含某些cookie - Cookie=chocolate, ch.p
Header 请求必须包含某些header - Header=X-Request-Id, \d+
Host 请求必须是访问某个host(域名) - Host=.somehost.org,.anotherhost.org
Method 请求方式必须是指定方式 - Method=GET,POST
Path 请求路径必须符合指定规则 - Path=/red/{segment},/blue/**
Query 请求参数必须包含指定参数 - Query=name, Jack或者- Query=name
RemoteAddr 请求者的ip必须是指定范围 - RemoteAddr=192.168.1.1/24
Weight 权重处理

4. 过滤器工厂

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:

  • 路由过滤器的种类:Spring提供了31种不同的路由过滤器工厂。例如:
    名称 说明
    AddRequestHeader 给当前请求添加一个请求头
    RemoveRequestHeader 移除请求中的一个请求头
    AddResponseHeader 给响应结果中添加一个响应头
    RemoveResponseHeader 从响应结果中移除有一个响应头
    RequestRateLimiter 限制请求的流量
  • 请求头过滤器:以AddRequestHeader 为例来讲解。

    需求:给所有进入userservice的请求添加一个请求头:Truth=itcast is freaking awesome!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 只需要修改gateway服务的application.yml文件,添加路由过滤即可
    spring:
    cloud:
    gateway:
    routes:
    - id: user-service # 当前过滤器写在userservice路由下,因此仅仅对访问userservice的请求有效
    uri: lb://userservice
    predicates:
    - Path=/user/**
    filters: # 过滤器
    - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
  • 默认过滤器:如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    spring:
    cloud:
    gateway:
    routes:
    - id: user-service
    uri: lb://userservice
    predicates:
    - Path=/user/**
    default-filters: # 默认过滤项
    - AddRequestHeader=Truth, Itcast is freaking awesome!

5. 全局过滤器

  1. 全局过滤器作用
    上一节学习的过滤器,网关提供了31种,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现。全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。
    定义方式是实现GlobalFilter接口。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface GlobalFilter {
    /**
    * 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
    *
    * @param exchange 请求上下文,里面可以获取Request、Response等信息
    * @param chain 用来把请求委托给下一个过滤器
    * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
    */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
    }
    在filter中编写自定义逻辑,可以实现下列功能:登录状态判断、权限校验、请求限流
  2. 自定义全局过滤器
    需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:(如果同时满足则放行,否则拦截)
    1、参数中是否有authorization,
    2、authorization参数值是否为admin
    实现:在gateway中定义一个过滤器:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Order(-1) // 过滤器优先级
    @Component
    public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 1.获取请求参数
    MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
    // 2.获取authorization参数
    String auth = params.getFirst("authorization");
    // 3.校验
    if ("admin".equals(auth)) {
    // 放行
    return chain.filter(exchange);
    }
    // 4.拦截
    // 4.1.禁止访问,设置状态码
    exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
    // 4.2.结束处理
    return exchange.getResponse().setComplete();
    }
    }
  3. 过滤器执行顺序
    请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
    请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
  • 排序的规则是什么呢?
    • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
    • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
    • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
    • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

6. 跨域问题

  1. 什么是跨域问题
    域名不一致就是跨域,主要包括:域名不同: www.taobao.comwww.taobao.orgwww.jd.com; 域名相同,端口不同:localhost:8080 和 localhost:8081
    跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
    解决方案:CORS https://www.ruanyifeng.com/blog/2016/04/cors.html
  2. 模拟跨域问题
    找到课前资料的页面文件:
    放入tomcat或者nginx这样的web服务器中,启动并访问。
    可以在浏览器控制台看到下面的错误:
    从localhost:8090访问localhost:10010,端口不同,显然是跨域的请求。
  3. 解决跨域问题:在gateway服务的application.yml文件中,添加下面的配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    spring:
    cloud:
    gateway:
    # ??
    globalcors: # 全局的跨域处理
    add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
    corsConfigurations:
    '[/**]':
    allowedOrigins: # 允许哪些网站的跨域请求
    - "http://localhost:8090"
    allowedMethods: # 允许的跨域ajax的请求方式
    - "GET"
    - "POST"
    - "DELETE"
    - "PUT"
    - "OPTIONS"
    allowedHeaders: "*" # 允许在请求中携带的头信息
    allowCredentials: true # 是否允许携带cookie
    maxAge: 360000 # 这次跨域检测的有效期

Sentinel 服务保护

  • 雪崩:微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。
    • 雪崩问题产生的原因是什么?
      微服务相互调用,服务提供者出现故障或阻塞。
      服务调用者没有做好异常处理,导致自身故障
      调用链中的所有服务级联失败,导致整个集群故障
    • 解决问题的思路有哪些?
      尽量避免服务出现故障或阻塞:保证代码的健壮性; 保证网络畅通; 能应对较高的并发请求;
      服务调用者做好远程调用异常的后备方案,避免故障扩散
    • 解决雪崩问题的常见方案有哪些?
      请尔限流:限制流量在服务可以处理的范围,避免因突发流量丽故障
      线程隔离:控制业务可用的线程数量,将故障隔离在一定范围
      失败处理:(熔断的一部分)定义fallback逻辑,让业务失败时不再抛出异常,而是走fallback逻辑
      服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走fallback
    • 技术实现
      现常用 Sentinel,功能更加强大;Hystrix 支持 SpringCloud 2020前的版本

分布式事务

https://www.bilibili.com/list/watchlater?oid=961238101&bvid=BV1kH4y1S7wz&spm_id_from=333.999.top_right_bar_window_view_later.content.click&p=46


RabbitMQ 消息队列【5672】

https://leo710aka.github.io/2024/01/11/RabbitMQ/


Docker

http://localhost:4000/2023/04/14/Docker/