devops(后端)
1.前言
该devpos架构为gitlab+jenkins+harbor+k8s,项目是java项目,流程为从gitlab拉取项目代码到jenkins,jenkins通过maven将项目代码打成jar包,通过dockerfile构建jdk环境的镜像并把jar包放到镜像中启动,构建好的镜像通过docker上传到harbor镜像仓库中,最后k8s通过更新镜像的方式去发布新的服务,此套流程通过jenkins的pipeline实现,其中还涉及多分支、回滚操作
2.部署环境
gitlab部署参考:gitlab部署_Apex Predator的博客-CSDN博客
jenkins部署参考:jenkins部署_Apex Predator的博客-CSDN博客
harbor部署参考:k8s harbor镜像仓库搭建_k8s镜像仓库_Apex Predator的博客-CSDN博客
k8s部署参考:kubeadm部署k8s 1.26.0版本高可用集群_Apex Predator的博客-CSDN博客
3.配置环境
jenkins主机配置
在部署jnekins环境的时候已经安装了jdk、maven、git环境,现在还需要在安装docker、kubectl,docker用于构建镜像和推送镜像到harbor仓库,kubectl用于操作k8s发布新版本
docker部署参考:部署docker-ce_Apex Predator的博客-CSDN博客
部署完成后需要配置一下docker的daemon.json文件(因为是私有仓库,docker是不允许拉取不安全仓库的镜像)
vi /etc/docker/daemon.json
{"registry-mirrors": ["https://sudzwtcw.mirror.aliyuncs.com"],"insecure-registries": ["harbor.apex.com"] #配置该项,使得docker允许访问不安全的镜像仓库
}
重启docker服务
systemctl restart docker
部署kubectl
配置阿里云的k8s yum源
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
安装kubectl
yum -y install kubectl-1.26.0
在jenkins上引用安装的环境
在jenkins中安装以下插件
Git
Git plugin
Git Parameter
GitLab
Credentials
Credentials Binding
CloudBees Docker Build and Publish plugin
Build With Parameters
Dynamic Extended Choice Parameter Plug-In
Dynamic Parameter Plug-in
Extended Choice Parameter
List Git Branches Parameter
Pipeline
Pipeline: Declarative
kubernetes
Kubernetes plugin
Kubernetes CLI Plugin
Kubernetes Credentials Plugin
在jenkins上配置ssh密钥(一路回车即可)
ssh-keygen
查看公钥并拷贝到gitlba上
cat ~/.ssh/id_rsa.pub
将以上公钥填入以下gitlab配置中即可
配置jenkins Credentials
创建gitlab凭据
查看jenkins的私钥
cat ~/.ssh/id_rsa #复制输出的所有内容
创建harbor凭据
创建k8s凭据
先在k8s下载密钥文件(就是集群的配置文件,里面包含有密钥和请求集群的接口等配置)
ls .kube/config
sz .kube/config
harbor配置
配置harbor仓库项目信息
分别创建两个项目,一个存放基础镜像叫base_image,一个存放后端服务镜像叫jdk
gitlab配置
创建项目
创建完成后上传项目代码
k8s集群配置
在k8s的node节点上配置docker的daemon.json文件
vi /etc/docker/daemon.json
{"registry-mirrors": ["https://sudzwtcw.mirror.aliyuncs.com"],"insecure-registries": ["harbor.apex.com"], #配置该项,使得docker允许访问不安全的镜像仓库"exec-opts": ["native.cgroupdriver=systemd"]
}
重启docker服务
systemctl restart docker
4.构建发布
使用jenkins的pipeline和webhook实现ci/cd自动化发布
jenkins配置
新建流水线项目
配置Choice Parameter,直接在Jenkins file中写parameter不手动配置parameter的话,第一次执行会失败,执行完后会自动生成parameter,所以还是手动也配置一下
配置webhook tigger用于触发自动构建
配置流水线脚本
选择pipeline script模式
填入以下脚本
pipeline { #配置pipelineagent any #客户端配置,jenkins可以使用k8s作为agent环境去发布,这里使用any为任意环境environment { #配置环境变量参数,用来给下面调用registry = "harbor.apex.com" #配置harbor仓库地址harbor_auth = "a1e2c627-dc62-4599-a035-8e98d74665ab" #配置harbor credentials凭据idproject = "jdk" #配置harbor仓库镜像项目app_name = "k8s-cs" #配置k8s的pod名称namespace = "k8s-cs" #配置k8s的命名空间k8s_auth = "k8s-kubeconfig" #配置k8s credentials凭据id }parameters { #配置parameters功能choice(choices: ['','master', 'apex'], description: '''choice branch''', name: 'branch') #配置分支选择的choice,用于回滚和手动发布的分支选择,此处默认时空值,避免下面阶段使用branch变量判断出现问题choice(choices: ['deploy', 'rollback'], description: '''deploy ---部署 rollback ---回滚''', name: 'status') #配置发布回滚选择的choice,用于选择发布还是回滚,此处默认时deploy发布}stages { #配置构建的每个阶段stage('Checkout code') { #配置从gitlab拉取代码parallel { #配置以下两个阶段的操作并行执行stage('webhook tigger') { #配置自动触发执行的操作when { #使用when判断env.gitlabBranch的值是否为空,使用webhook自动构建,是通过env.gitlabBranch变量来赋予分支值,判断该值是否为空,就能判断是通过webhook构建还是手动构建的expression { params.status == 'deploy' && env.gitlabBranch != null }} #params的值用来判断,是否执行的是发布操作,如果是发布操作才执行该操作,使用&&是需要两个条件都为true才执行steps { #以上判断通过后执行从gitlab对应的分支拉取代码的操作checkout([$class: 'GitSCM', branches: [[name: '${env.gitlabBranch}']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab_auth', url: 'git@10.1.60.114:gitlab-instance-c484dcfc/java-project.git']]]) #可以看到branch使用的是env.gitlabBranch这个变量,因为webhook自动构建是将分支的值赋予到这个变量中,credentialsId就配置gitlab的credentials凭据idsh "git branch" #这里是测试是否能获取到分支的值echo "Current branch: ${env.gitlabBranch}" #这里是输出分支的值script { #提取gitlab的项目id,用来制作镜像tagcommit_id = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()tag = BUILD_TAG + '-' + commit_id #使用jenkins的构建标签和gitlab的项目id作为镜像标签的tag}}}stage('jenkins scm') { #此处就是判断是否用的手动构建when { #使用when判断env.gitlabBranch是否等于空值,是空值就证明是通过手动构建的expression { params.status == 'deploy' && env.gitlabBranch == null }} #params的值用来判断,是否执行的是发布操作steps { #执行拉取gitlab代码操作checkout([$class: 'GitSCM', branches: [[name: '${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab_auth', url: 'git@10.1.60.114:gitlab-instance-c484dcfc/java-project.git']]]) #这里和上面自动构建的命令没有什么区别就除了branches使用branch作为变量,因为我们上面配置parameters,手动构建需要选择名为branch的choice去选择分支,所以我们就通过该变量获取到选择的分支名称sh "git branch"echo "Current branch: ${branch}"script {commit_id = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()tag = BUILD_TAG + '-' + commit_id}}}} #到这里parallel并行操作就结束了} #到这里上面的拉取代码操作就结束了stage('Build jar') { #接下来就是编译代码打包的操作步骤when { #使用when判断是否是发布操作,回滚是不用执行这个阶段的,上面的拉代码操作也是expression { params.status == 'deploy' }}steps { #when判断通过后执行以下命令打包,打包命令可能会不一样,可以咨询一下开发sh "mvn clean install -DskipTests" #编译打包命令sh "ls target/" #查看是否有打出来jar包}}stage('docker image build and push') { #构建镜像和推送镜像到harbor仓库步骤when {expression { params.status == 'deploy' }}steps {withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { #使用harbor的credentials凭据,因为推镜像和拉基础镜像都是需要访问harbor仓库的sh "docker build -t ${registry}/${project}/${app_name}:${tag} ."sh "docker push ${registry}/${project}/${app_name}:${tag}"} #可能生产、测试环境打包的镜像名称要求不一样,这时就需要跟上面一样使用when去判断是哪个环境构建镜像}}stage('k8s update iamge version') { #发布版本操作parallel { #以上有讲解此参数stage ('to apex') {when { #判断分支,决定发布到哪个环境中expression { params.status == 'deploy' && ( env.gitlabBranch == 'apex' || params.branch == 'apex' ) }} #后面用了一个|| 或的判断,即两个获取分支的变量只要有一个成立即可,主要是自动构建和手动构建的分支变量不一样,所以必须得使用两个分支变量判断steps { #配置k8s集群得credentials凭据,通过凭据调用k8s集群的apiserverwithCredentials([file(credentialsId: "${k8s_auth}", variable: 'KUBECONFIG')]) {sh "kubectl --kubeconfig ${KUBECONFIG} set image deployment -l app=${app_name} ${app_name}=${registry}/${project}/${app_name}:${tag} -n ${namespace} --record"} #使用kubectl命令指定k8s集群的配置文件执行更新镜像命令的操作,更新镜像操作即发布版本,并记录该操作用于回滚}}stage('to master') {when { #判断分支,决定发布到哪个环境中expression { params.status == 'deploy' && ( env.gitlabBranch == 'master' || params.branch == 'master' ) }}steps {withCredentials([file(credentialsId: "${k8s_auth}", variable: 'KUBECONFIG')]) {sh "kubectl --kubeconfig ${KUBECONFIG} get pod -n ${namespace}"sh "echo ${params.branch}"sh "echo ${env.gitlabBranch}"} #以上命令是测试用的,要是发版命令改为以上的set命令即可}}}}stage('rollback version') { #回滚操作,当发布的版本有问题时,会需要使用到回滚操作parallel {stage('to apex') {when { #判断params.status变量是否为rollback,且判断分支,因为回滚操作都是手动操作的所以使用branch变量判断分支即可expression { params.status == 'rollback' && params.branch == 'apex' }}steps { #此处也是配置k8s集群的credentials凭据withCredentials([file(credentialsId: "${k8s_auth}", variable: 'KUBECONFIG')]) {sh "kubectl --kubeconfig ${KUBECONFIG} rollout undo deployment ${app_name} -n ${namespace}"} #使用rollout命令并指定deployment回滚版本} }stage('to master') {when {expression { params.status == 'rollback' && params.branch == 'master' }}steps {withCredentials([file(credentialsId: "${k8s_auth}", variable: 'KUBECONFIG')]) {sh "kubectl --kubeconfig ${KUBECONFIG} get deployment ${app_name} -n ${namespace}"} #此处也是测试命令,回滚命令参照上面写即可} }}}}
}
制作基础镜像(打包的时候直接使用基础镜像会更快)
vim dockerfile
FROM alpine:latest #拉取系统镜像
ENV TZ="Asia/Shanghai" #这个环境变量用于下面配置时区
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \ #配置源
&& apk add --upgrade --no-cache openjdk8 tzdata ttf-dejavu fontconfig \ #安装jdk环境、字体等工具
&& cp /usr/share/zoneinfo/${TZ} /etc/localtime \ #配置时区
&& echo ${TZ} > /etc/timezone
构建镜像
docker build -t harbor.apex.com/base_image/jdk8_image:latest .
上传镜像
docker push harbor.apex.com/base_image/jdk8_image:latest
gitlab配置
在项目代码仓库中创建dockerfile用于构建发布镜像
dockerfile内容如下
FROM harbor.apex.com/base_image/jdk8_image:latest #使用harbor仓库中的基础镜像
ENV JVM_OPTS="-Xms512m -Xms512m" #配置关于java的环境变量
ENV HEAP_DUMP_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/java_jar/log"
RUN mkdir -p /opt/java_jar/log #创建存放jar包目录还有日志目录
WORKDIR /opt/java_jar/ #设置存放jar包目录为工作目录,即相当于cd到这个目录上
COPY ./target/*.jar ./ #拷贝jar包到镜像中,target目录是在jenkins上打包后,jar包的生成目录
EXPOSE 8761 #配置需要暴露的端口
ENTRYPOINT java ${JVM_POST} ${HEAP_DUMP_OPTS} -jar *.jar #配置启动jar包服务
配置webhook用于触发jenkins自动化构建
k8s集群 配置(在master节点执行)
创建namespace
kubectl create namespace k8s-cs
创建secret用于拉取harbor仓库的镜像使用
kubectl create secret docker-registry harbor-secret --namespace=k8s-cs --docker-server=https://harbor.apex.com --docker-username=admin --docker-password=Harbor12345
查看secret
kubectl get secret -n k8s-cs
创建yaml
vim k8s-cs.yaml
apiVersion: apps/v1
kind: Deployment
metadata: labels:app: k8s-csname: k8s-csnamespace: k8s-cs
spec:replicas: 5progressDeadlineSeconds: 600minReadySeconds: 10strategy: #配置滚动更新策略rollingUpdate:maxSurge: 1maxUnavailable: 0type: RollingUpdateselector:matchLabels:app: k8s-cstemplate:metadata:labels:app: k8s-csspec:containers:- name: k8s-csimage: harbor.apex.com/jdk/k8s-cs #填写harbor仓库的镜像地址imagePullPolicy: IfNotPresent ports:- containerPort: 8761readinessProbe: #配置就绪探针httpGet:path: /port: 8761scheme: HTTPinitialDelaySeconds: 30periodSeconds: 10timeoutSeconds: 2successThreshold: 1failureThreshold: 2livenessProbe: #配置存活探针tcpSocket:port: 8761initialDelaySeconds: 30periodSeconds: 10successThreshold: 1timeoutSeconds: 2failureThreshold: 2 imagePullSecrets: #配置拉取镜像使用的secret- name: harbor-secretrestartPolicy: Always
---
apiVersion: v1
kind: Service #配置service映射端口
metadata:name: k8s-csnamespace: k8s-cs
spec:selector:app: k8s-cstype: NodePort clusterIP:ports:- port: 8761targetPort: 8761nodePort: 30002protocol: TCP
执行yaml创建pod
kubectl apply -f k8s-cs.yaml
5.测试发布、回滚
手动构建
回滚
自动化构建
更改一下分支合并到master看看会不会触发自动化构建
更改的过程我就省略了,更改好之后创建合并请求
批准且合并后触发Jenkins构建
这个也是多分支的发布,合并其它分支也是可以触发,我就不演示了
6.补充
pipeline有两种模式,还有一种是pipeline script from scm,就是在流水线配置界面就编辑好从gitlab仓库拉代码的信息,并且把jenkinsfile放到gitlab的仓库中,跟着代码一起拉下来,接下来也说一下这种方式的使用
创建pipeline项目,这里就不贴图了
配置git parameter,用来选择发布时的分支
在gitlab的项目上创建jenkinsfile文件
jenkinsfile脚本如下
pipeline {agent anyenvironment {registry = "harbor.apex.com"harbor_auth = "a1e2c627-dc62-4599-a035-8e98d74665ab"project = "jdk"app_name = "k8s-cs"namespace = "k8s-cs"image_url = "${registry}/${project}/${app_name}:${tag}"k8s_auth = "k8s-kubeconfig"k8s_name = "kubernetes"} #这里和之前的pipeline不同,因为配置了scm后在jenkinsfile中就不需要再配置拉取代码的步骤了,其它的配置都是一样的,不过我这是一个简化版的适合单分支stages {stage('Build jar') {steps {sh "mvn clean install -DskipTests"sh "ls target/"}}stage('docker image build and push') {steps {withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {sh "docker build -t ${image_url} ."sh "docker push ${image_url}"}}}stage('push yaml to k8s') {steps {withCredentials([file(credentialsId: "${k8s_auth}", variable: 'KUBECONFIG')]) {sh "kubectl --kubeconfig ${KUBECONFIG} set image deployment -l app=${app_name} ${app_name}=${image_url} -n ${namespace} --record"}}}}
}
去执行构建就可以了,这里是jenkins项目通过scm的gitlab拉取项目代码,拉取了项目后再去执行流水线的jenkinsfile脚本,所以jenkinsfile里是不需要配置拉代码这个动作的
以下再 讲解一下通过jenkins agent来执行流水线,这里讲诉使用k8s集群的pod作为jenkins agent去发布,大概流程就是使用k8s插件连接k8s集群,调用k8s的api server去创建jenkins agent,流水线的所有步骤都在jenkins agent上执行,期间还需要配置所有需要用的环境的pod,这里就不详细介绍,就说一下踩到的坑
jenkins agent使用的jnlp镜像的jdk环境需要和jenkins的jdk环境一致,不然是没办法连接的
使用jenkins agent除了要打开jenkins 50000端口与Jenkins agent通信外,还需要配置Git Host Key Verification Configuration项,不然的话在从gitlab拉代码的适合会一直报错
里面填写的密钥在 known_hosts文件中,配置的是gitlab主机的密钥 ,一定要配置此项不然pod拉不到gitlab的项目代码
cat /root/.ssh/known_hosts
还有就是使用docker镜像,镜像里的docker是没办法配置daemon.json的,所以访问不到harbor的私有仓库,除非harbor使用的是公有证书和公有域名
相关文章:

devops(后端)
1.前言 该devpos架构为gitlabjenkinsharbork8s,项目是java项目,流程为从gitlab拉取项目代码到jenkins,jenkins通过maven将项目代码打成jar包,通过dockerfile构建jdk环境的镜像并把jar包放到镜像中启动,构建好的镜像通…...

Ubuntu安装企业微信
Ubuntu安装企业微信_ubuntu下安装企业微信_星光2020的博客-CSDN博客 在Ubuntu环境安装企业微信可以参考 https://github.com/zq1997/deepin-wine 所述的方法 首先运行 $ wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 然后就可以像安装其它软件一样安装企业微信或其它…...

Prometheus 的应用服务发现及黑河部署等
目录 promtool检查语法 部署Prometheus Server 检查语法是否规范 部署node-exporter 部署Consul 直接请求API进行服务注册 使用register命令注册服务(建议使用) 单个和多个注册,多个后面多加了s 在Prometheus上做consul的服务发现 部署…...

JAVA SE -- 第十二天
(全部来自“韩顺平教育”) 常用类 一、包装类 1、包装类的分类 ①针对八种基本数据类型相应的引用类型--包装类 ②有了类的特点,就可以调用类中的方法 ③ 基本数据类型包装类booleanBooleancharCharacterbyteByteshortShortintInteger…...

实战:工作中对并发问题的处理
大家好,我是 方圆。最近在接口联调时发生了数据并发修改问题,我想把这个问题讲解一下,并把当时提出的解决方案进行实现,希望它能在大家以后在遇到同样的问题时提供一些借鉴和思考的方向。原文还是收录在我的 Github: enthusiasm 中…...

腾讯云Cloud Studio:基于Claude快速完成Excel工资自动核算
目录 1 什么是Cloud Studio?2 注册与代码管理2.1 账号注册2.2 Git关联 3 实战:Excel工资自动核算3.1 创建项目与配置3.2 “念咒师”Claude GPT3.3 代码编写与运行 1 什么是Cloud Studio? Cloud Studio是腾讯云为开发者提供的一个基于浏览器的…...

Spring Boot OAuth2 快速入门示例
系统要求 Spring Authorization Server 需要JDK1.8及以上版本。 项目搭建 使用在线项目初始化器 https://start.spring.io/ 生成项目[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ljKbMI4H-1690726855433)(images/screenshot_1690602511482.png)…...

MethodInterceptor
目录 1 MethodInterceptor 1.1 HandleSync 1.2 HandleException 1.3 /// This will be called via Reflection MethodInterceptor HandleSync private void HandleSync(IReadOnlyList<MethodFilterAttribute> filterAttributes, IReadOnlyList<ExceptionFilte…...

PID模块化__以stm32直流电机速度为例
文章目录 前言一、相关PID源码.c.h 二、如何使用1.创建变量2.初始化3.运算4.修改pid参数 总结 前言 本篇使用到的基于这个STM32CubeMX 直流电机PID速度控制、HAL库、cubemx、PID、速度控制、增量式 由于上次使用的pid没有模块化,当多出使用pid的时候就会很麻烦 所以…...

Java ~ Collection/Executor ~ DelayQueue【总结】
前言 文章 相关系列:《Java ~ Collection【目录】》(持续更新)相关系列:《Java ~ Executor【目录】》(持续更新)相关系列:《Java ~ Collection/Executor ~ DelayQueue【源码】》(学…...

前端高级面试题-安全相关
1 XSS 跨⽹站指令码(英语: Cross-site scripting ,通常简称为: XSS )是⼀种⽹站应⽤程式的安全漏洞攻击,是代码注⼊的⼀种。 它允许恶意使⽤者将程式码注⼊到⽹⻚上,其他使⽤者在观看⽹⻚时就会…...

【前缀和】560.和为 K 的子数组
Halo,这里是Ppeua。平时主要更新C,数据结构算法,Linux与ROS…感兴趣就关注我bua! 和为K的子数组 题目:示例:题解:解法一:解法二: 题目: 示例: 题解: 解法一: 暴力解法:我们很容易想到通过两个for循环去遍…...

【Docker】安全及日志管理
安全及日志管理 Docker 安全及日志管理一:Docker 容器与虚拟机的区别1. 隔离与共享2. 性能与损耗 二:Docker 存在的安全问题1.Docker 自身漏洞2.Docker 源码问题 三:Docker 架构缺陷与安全机制1. 容器之间的局域网攻击2. DDoS 攻击耗尽资源3.…...

基于x-scan扫描线的3D模型渲染算法
基于x-scan算法实现的z-buffer染色。c#语言,.net core framework 3.1运行。 模型是读取3D Max的obj模型。 x-scan算法实现: public List<Vertex3> xscan() {List<Vertex3> results new List<Vertex3>();SurfaceFormula formula g…...

LeetCode36.Valid-Sudoku<有效的数独>
题目: 思路: 这题并不难,它类似于N皇后问题。在N皇后问题中,行,列,对角线,写对角线,都不能出现连续的皇后。 本题类似,不过他是行,列,还有一个B…...

Linux中的pause函数
2023年7月29日,周六上午 函数原型 在Linux中,pause()函数用于使当前进程暂停执行,直到接收到一个信号。 #include <unistd.h>int pause(void);pause()函数不接受任何参数。 通常,pause()函数用于编写简单的信号处理程序&…...

CommonCollections6链分析
前面和CC1一样 优点是不限制jdk版本和cc的版本 先开一个ChainedTransformer 然后创LazyMap 我们顺便执行一下避免上面写错 能弹计算器 没问题 后面就是CC6不同的地方了 我们需要一个TiedMapEntry 因为需要一个类调用了get方法 在TiedMapEntry的getValue()方法中调用了get()…...

优化基于tcp,socket的ftp文件传输程序
原始程序: template_ftp_server_old.py: import socket import json import struct import os import time import pymysql.cursorssoc socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST 192.168.31.111 PORT 4101 soc.bind((HOST,PORT)) p…...

MySQL 数据库 【增删查改(二)】
目录 一、表的设计 1、一对一 2、一对多 3、多对多 二、新增 三、查询 1、聚合查询 (1)聚合函数: (2) group by 子句 (3)having 2、联合查询 (1)内连接 (2)外连接 (3)自链接 (4)…...

力扣 -- 978. 最长湍流子数组
一、题目 二、解题步骤 下面是用动态规划的思想解决这道题的过程,相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码 class Solution { public:int maxTurbulenceSize(vector<int>& nums) {int nnums.size();vector<int> f(n);…...

甘特图 Dhtmlx Gantt
介绍 在一些任务计划、日程进度等场景中我们会使用到甘特图,Dhtmlx Gantt 对于甘特图的实现支持很友好,文档API介绍全面,虽然增强版的收费,但免费版的足以够用。 官网:https://docs.dhtmlx.com/gantt/ 安装dhtml gannt…...

iOS 应用上架流程详解
iOS 应用上架流程详解 欢迎来到我的博客,今天我将为大家分享 iOS 应用上架的详细流程。在这个数字化时代,移动应用已经成为了人们生活中不可或缺的一部分,而 iOS 平台的 App Store 则是开发者们发布应用的主要渠道之一。因此,了解…...

Python入门【LEGB规则、面向对象简介、面向过程和面向对象思想、面向对象是什么? 对象的进化 、类的定义、对象完整内存结构 】(十三)
👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误…...

【消息中间件】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务
文章目录 视频演示效果前言一、分析二、全局注入MQTT连接1.引入库2.写入全局连接代码 二、PHP环境建立总结 视频演示效果 【uniapp】实现买定离手小游戏 前言 Mqtt不同环境问题太多,新手可以看下 《【MQTT】Esp32数据上传采集:最新mqtt插件(支…...

【C语言初阶】指针篇—上
目录 1. 指针是什么?2. 指针和指针类型2.1 指针-整数2.2 指针的解引用 3. 野指针3.1 野指针成因1. 指针未初始化2. 指针越界访问3. 指针指向的空间释放 3.2 如何规避野指针 1. 指针是什么? 指针是什么? 指针理解的2个要点: > 1…...

基于FasterRCNN深度学习网络的车辆检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心程序 ....................................................................... % 训练Faster R-…...

机器学习深度学习——多层感知机
👨🎓作者简介:一位即将上大四,正专攻机器学习的保研er 🌌上期文章:机器学习&&深度学习——感知机 📚订阅专栏:机器学习&&深度学习 希望文章对你们有所帮助 上一节…...

Django模型将模型注释同步到数据库
1、安装django-comment-migrate库 pip install django-comment-migrate 2、将库注册到settings.py文件中 INSTALLED_APPS [...django_comment_migrate, # 表注释... ] 3、加注释 3.1、给模型(表)加注释 在模型的class Meta中编辑 verbose_name&…...

STM32 Flash学习(二)
STM32F1的官方固件库操作FLASH的几个常用函数。这些函数和定义分布在源文件stm32f1xx_hal_flash.c/stm32f1xx_hal_flash_ex.c以及头文件stm32f1xx_hal_flash.h/stm32f1xx_hal_flash_ex.h中。 锁定解函数 对FLASH进行写操作前必须先解锁,解锁操作:在FLA…...

kotlin获取泛型集合的类型信息
通过 reified 关键字和内联函数来实现 inline fun <reified T> getClassFromList(list: List<T>): Class<T> {return T::class.java }fun main() {val list listOf("Hello", "World")val clazz getClassFromList(list)println(clazz)…...