K8S初级入门系列之十-控制器(StatefulSet)

news2024/10/9 0:52:25

一、前言

     在前面的系列K8S初级入门系列之六-控制器(RC/RS/Deployment),K8S初级入门系列之七-控制器(Job/CronJob/Daemonset)我们已经介绍了多种控制器,今天我们将介绍最后一种控制器--StatefulSet,顾名思义,即有状态Set,那什么是无状态和有状态呢?有个形象的比如,牛和宠物。对于牛来说,只要能干活,在农场主眼里没有区别,一头牛死掉了,可以用另外一头牛顶替上,农场主不会纠结是否还是之前那头牛。对于宠物来说,他们的主人是能感受到个体之间区别的,一只宠物死掉了,是无法用另外一只替换。

    前面介绍的RC,RS控制器所创建的都是无状态的实例,即实例个体之间没有差别,一旦某个实例挂掉了,可以启动另一个实例顶替上,比如Ngnix实例。但在实际工程中,还有一种是有状态实例,比如数据库,分库1实例和分库2实例是不一样的,分库1挂掉了,是不能直接启动一个实例顶替上的,而是要确保新启的实例在网络标识,主机标识,存储数据等方面保持一致,才能不影响业务运行。

     显然,对于以上有状态的实例控制,RC,RS控制器是无法做到的,针对有状态场景,K8S提供StatuefulSet控制器。

二、StatefulSet原理

    StatefulSet适用于有状态实例,那么它是如何确保状态的一致呢?还是以数据库的分库为例,需要保持网络标识,主机标识,存储卷一致。如果用之前的RS来实现,看下实现的原理图:

      需要为每个实例创建一个ReplicaSet,分配独立的PVC,以及提供对外访问的Service。这个方式虽然可以实现需求,但并不是完美的解决方案,如果分库数量过大(超过100),那么维护的工作量将非常大。

  接下来,我们看下StatefulSet如何实现

    StatefuSet负责创建不同实例的Pod,每个实例按照数字顺序进行标识,同时分配与实例关联的PVC,以及Service。

      当某个实例挂死后,重建实例,并写入旧实例的标识,并关联就实例的PVC,虽然实例的实体发生变化,但是对外业务感知不到的,还是同一实例,可以理解为旧实例的复制体。如下图所示:

 三、StatefulSet创建

在创建前,我们首先预备存储PV,以及StatefulSet所依赖的Headless Service。

1、创建PV

     这里我们创建两个PV,分别为a-pv.yaml和b-pv.yaml,以前一章节创建的NFS为存储卷,内容如下:

  • a-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: a-pv
  labels:
    pv: a-pv
spec:
  capacity:
    storage: 10Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfs/data/a
    server: 192.168.16.4
  • b-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: b-pv
  labels:
    pv: b-pv
spec:
  capacity:
    storage: 10Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfs/data/b
    server: 192.168.16.4

执行该文件,并查看状态

[root@k8s-master yaml]# kubectl apply -f a-pv.yaml 
persistentvolume/a-pv created
[root@k8s-master yaml]# kubectl apply -f b-pv.yaml 
persistentvolume/b-pv created
[root@k8s-master yaml]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                     STORAGECLASS   REASON   AGE
a-pv                                       10Mi       RWX            Recycle          Available                                                     13s
b-pv                                       10Mi       RWX            Recycle          Available                                                     4s

PV创建完成,目前处于Available状态。 

 2、创建Headless Service

      StatefulSet需要配套Headless Service,其相关知识可以参见:K8S初级入门系列之八-网络

 其yaml内容如下:

[root@k8s-master yaml]# cat stateful-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: stateful-svc
  labels:
    app: stateful-svc
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx-stateful

   其中clusterIP: None表示的就是Headless Service,执行该文件,并查看状态

[root@k8s-master yaml]# kubectl get svc
NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
stateful-svc                   ClusterIP      None            <none>          80/TCP         23h

   3、创建StatefulSet

     接下来就来创建今天的主角StatefulSet,其内容如下:

[root@k8s-master yaml]# cat nginx-stateful.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-statefulset
spec:
  serviceName: "stateful-svc"
  replicas: 2
  selector:
    matchLabels:
      app: nginx-stateful
  template:
    metadata:
      labels:
        app: nginx-stateful
    spec:
      containers:
      - name: nginx-stateful
        image:  nginx:1.14.2
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteMany" ]
      resources:
        requests:
          storage: 10Mi

      StatefulSet与Deployment的结构类似,这里定义了replics为2,也就是两个副本,容器镜像为 nginx:1.14.2,并将目录/usr/share/nginx/html挂载到PVC存储上。

    执行该文件,并查看创建状态:

[root@k8s-master yaml]# kubectl apply -f nginx-stateful.yaml 
statefulset.apps/nginx-statefulset created
[root@k8s-master yaml]# kubectl get statefulset
NAME                READY   AGE
nginx-statefulset   2/2     5m14s

我们再看下pod的状态

[root@k8s-master yaml]# kubectl get pod
NAME                                      READY   STATUS                   RESTARTS           AGE
nginx-statefulset-0                       1/1     Running                  0                  5s
nginx-statefulset-1                       1/1     Running                  0                  3s

此时,成功创建了2个Pod,并按索引顺序作为名称的后缀。我们再来看下PVC和PV

[root@k8s-master yaml]# kubectl get pvc
NAME                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-nginx-statefulset-0   Bound    a-pv                                       10Mi       RWX                           6m43s
www-nginx-statefulset-1   Bound    b-pv                                       10Mi       RWX                           2m26s
[root@k8s-master yaml]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                             STORAGECLASS   REASON   AGE
a-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-0                           10m
b-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-1                           10m

自动完成了PVC的创建,并且PVC和PV都显示为绑定状态,说明存储卷已经挂载成功。

4、Pod的验证

验证下Pod的访问以及功能是否可用。

在K8S初级入门系列之八-Service核心概念章节,安装了dnsutils工具,使用该工具查看下这两个Pod的ip和域名。

[root@k8s-master ~]# kubectl exec -it dnsutils /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # nslookup nginx-statefulset-0.stateful-svc.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   nginx-statefulset-0.stateful-svc.default.svc.cluster.local
Address: 10.244.36.71

/ # nslookup nginx-statefulset-1.stateful-svc.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   nginx-statefulset-1.stateful-svc.default.svc.cluster.local
Address: 10.244.36.114

      StatefulSet Pod的访问域名格式为Pod名称.Service名称.default.svc.cluster.local。通过nsloolup正确的解析了域名的IP,内部应用就可以使用域名访问不同Pod了。

接下来,我们验证下Pod的访问,首先分别给html写入不同的内容

[root@k8s-master ~]# kubectl exec nginx-statefulset-0 -- sh -c 'echo "this is nginx-statefulset-0" > /usr/share/nginx/html/index.html'
[root@k8s-master ~]# kubectl exec nginx-statefulset-1 -- sh -c 'echo "this is nginx-statefulset-1" > /usr/share/nginx/html/index.html'

为了方便起见,我们使用curl 名称访问这两个Pod

[root@k8s-master ~]# curl http://10.244.36.71
this is nginx-statefulset-0
[root@k8s-master ~]# curl http://10.244.36.114
this is nginx-statefulset-1

能正确的访问到内容。我们再看下NFS挂载目录的内容

[root@k8s-master data]# cd a
[root@k8s-master a]# cat index.html 
this is nginx-statefulset-0
...
[root@k8s-master data]# cd b
[root@k8s-master b]# cat index.html 
this is nginx-statefulset-1

 也已经存储了正确的内容。

四、Pod重建

      前面介绍,当其中一个Pod发生异常,为了确保副本数的一致,StatefulSet重建需要重建该Pod,且新建的Pod会保持主机标识,网络标识,以及存储等状态不变。下面我们将通过删除Pod(nginx-statefulset-0)来模拟。

 为了监控Pod的重建过程,我们打开两个终端

终端1,执行删除Pod指令

[root@k8s-master b]# kubectl delete pod nginx-statefulset-0
pod "nginx-statefulset-0" deleted

终端2,监控Pod的变化。

[root@k8s-master yaml]# kubectl get pod -w -l app=nginx-stateful
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          23h
nginx-statefulset-1   1/1     Running   0          23h
nginx-statefulset-0   1/1     Terminating   0          23h
nginx-statefulset-0   1/1     Terminating   0          23h
nginx-statefulset-0   0/1     Terminating   0          23h
nginx-statefulset-0   0/1     Terminating   0          23h
nginx-statefulset-0   0/1     Terminating   0          23h
nginx-statefulset-0   0/1     Pending       0          0s
nginx-statefulset-0   0/1     Pending       0          0s
nginx-statefulset-0   0/1     ContainerCreating   0          0s
nginx-statefulset-0   0/1     ContainerCreating   0          1s
nginx-statefulset-0   1/1     Running             0          2s

     可以看到,老的Pod变为Terminating后,新的Pod开始重建,状态变化Pending->ContainerCreating->Running。

    重建过程中,Pod的名称保持不变,再看下重建后,新Pod的网络标识。

[root@k8s-master ~]# kubectl exec -it dnsutils /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # nslookup nginx-statefulset-0.stateful-svc.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   nginx-statefulset-0.stateful-svc.default.svc.cluster.local
Address: 10.244.36.70

IP发生了变化,域名没有变化,所以在内部访问时,要使用域名访问,不能使用IP

继续看下挂载的PVC,可以使用详情查看

[root@k8s-master b]# kubectl describe pod nginx-statefulset-0
...
Volumes:
  www:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  www-nginx-statefulset-0
    ReadOnly:   false
....

绑定到了之前的PVC www-nginx-statefulset-0。再访问index内容

[root@k8s-master ~]# curl http://10.244.36.70
this is nginx-statefulset-0

也能正确访问到老Pod写入的内容。由此可见,重建的Pod保持了存储标识的一致。

五、扩缩容

     StatefulSet与RS一样,支持扩缩容。

1、扩容

先来看下扩容场景,我们需要扩容两个副本,首先创建两个PV,其yaml内容如下:

  • c-pv.yaml
[root@k8s-master yaml]# cat c-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: c-pv
  labels:
    pv: c-pv
spec:
  capacity:
    storage: 10Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfs/data/c
    server: 192.168.16.4
  • d-pv.yaml
[root@k8s-master yaml]# cat d-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: d-pv
  labels:
    pv: d-pv
spec:
  capacity:
    storage: 10Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfs/data/d
    server: 192.168.16.4

执行完成后,查看状态

[root@k8s-master yaml]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                             STORAGECLASS   REASON   AGE
a-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-0                           2d10h
b-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-1                           2d10h
c-pv                                       10Mi       RWX            Recycle          Available                                                             6m35s
d-pv                                       10Mi       RWX            Recycle          Available                                                             2m9s

新创建的两个PV处于Available状态。接下来启动两个终端

终端1,执行扩容指令

[root@k8s-master yaml]# kubectl scale  sts nginx-statefulset --replicas=4
statefulset.apps/nginx-statefulset scaled

终端2,监控pod的变化

[root@k8s-master yaml]# kubectl get pod -w -l app=nginx-stateful
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          2d11h
nginx-statefulset-1   1/1     Running   0          3d11h
nginx-statefulset-2   0/1     Pending   0          0s
nginx-statefulset-2   0/1     Pending   0          0s
nginx-statefulset-2   0/1     Pending   0          1s
nginx-statefulset-2   0/1     ContainerCreating   0          1s
nginx-statefulset-2   0/1     ContainerCreating   0          2s
nginx-statefulset-2   1/1     Running             0          3s
nginx-statefulset-3   0/1     Pending             0          0s
nginx-statefulset-3   0/1     Pending             0          0s
nginx-statefulset-3   0/1     Pending             0          1s
nginx-statefulset-3   0/1     ContainerCreating   0          1s
nginx-statefulset-3   0/1     ContainerCreating   0          2s
nginx-statefulset-3   1/1     Running             0          3s

 可以看到,以索引递增的命名方式依次新扩容了两个Pod。

再看下PVC和PV的状态

[root@k8s-master yaml]# kubectl get pvc
NAME                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-storage-pvc           Bound    pvc-f795e09b-1583-436b-869b-88a68d28ce64   1Gi        RWX            nfs-client     11d
www-nginx-statefulset-0   Bound    a-pv                                       10Mi       RWX                           3d11h
www-nginx-statefulset-1   Bound    b-pv                                       10Mi       RWX                           3d11h
www-nginx-statefulset-2   Bound    c-pv                                       10Mi       RWX                           6m27s
www-nginx-statefulset-3   Bound    d-pv                                       10Mi       RWX                           6m24s
[root@k8s-master yaml]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                             STORAGECLASS   REASON   AGE
a-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-0                           3d11h
b-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-1                           3d11h
c-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-2                           25h
d-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-3                           25h

 PVC状态正确,并与PV绑定。

同样,验证下新扩容的nginx-statefulset-2是否可用

[root@k8s-master yaml]# kubectl exec nginx-statefulset-2 -- sh -c 'echo "this is nginx-statefulset-2" > /usr/share/nginx/html/index.html'
[root@k8s-master yaml]# curl http://10.244.36.85 
this is nginx-statefulset-2

 能正确的写入和读取,扩容成功。

2、缩容

再将这两个Pod缩容掉,同样启用两个终端

终端1,执行缩容指令

[root@k8s-master yaml]# kubectl scale  sts nginx-statefulset --replicas=2
statefulset.apps/nginx-statefulset scaled

终端2,监控pod的变化

[root@k8s-master yaml]# kubectl get pod -w -l app=nginx-stateful
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          2d11h
nginx-statefulset-1   1/1     Running   0          3d11h
nginx-statefulset-2   1/1     Running   0          15m
nginx-statefulset-3   1/1     Running   0          14m

nginx-statefulset-3   1/1     Terminating   0          16m
nginx-statefulset-3   1/1     Terminating   0          16m
nginx-statefulset-3   0/1     Terminating   0          16m
nginx-statefulset-3   0/1     Terminating   0          16m
nginx-statefulset-3   0/1     Terminating   0          16m
nginx-statefulset-2   1/1     Terminating   0          16m
nginx-statefulset-2   1/1     Terminating   0          16m
nginx-statefulset-2   0/1     Terminating   0          16m
nginx-statefulset-2   0/1     Terminating   0          16m
nginx-statefulset-2   0/1     Terminating   0          16m

  可以看到,Pod按照倒序索引依次终止。

六、滚动更新

     StatefulSet支持更新,可以通过配置相关的更新策略,默认为"RollingUpdate"(滚动更新),如下所示:

spec:
  updateStrategy:
    type: RollingUpdate
 ....

接下来,我们通过滚动更新的方式将nginx的镜像版本升级到最新状态

终端1,执行更新执行,修改镜像的版本

[root@k8s-master yaml]# kubectl set image sts nginx-statefulset nginx-stateful=nginx:latest
statefulset.apps/nginx-statefulset image updated

终端2,监控Pod的变化

[root@k8s-master ~]# kubectl get pod -w -l app=nginx-stateful
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          2d13h
nginx-statefulset-1   1/1     Running   0          3d12h
nginx-statefulset-1   1/1     Terminating   0          3d12h
nginx-statefulset-1   1/1     Terminating   0          3d12h
nginx-statefulset-1   0/1     Terminating   0          3d12h
nginx-statefulset-1   0/1     Terminating   0          3d12h
nginx-statefulset-1   0/1     Terminating   0          3d12h
nginx-statefulset-1   0/1     Pending       0          0s
nginx-statefulset-1   0/1     Pending       0          0s
nginx-statefulset-1   0/1     ContainerCreating   0          0s
nginx-statefulset-1   0/1     ContainerCreating   0          1s
nginx-statefulset-1   1/1     Running             0          71s
nginx-statefulset-0   1/1     Terminating         0          2d13h
nginx-statefulset-0   1/1     Terminating         0          2d13h
nginx-statefulset-0   0/1     Terminating         0          2d13h
nginx-statefulset-0   0/1     Terminating         0          2d13h
nginx-statefulset-0   0/1     Terminating         0          2d13h
nginx-statefulset-0   0/1     Pending             0          0s
nginx-statefulset-0   0/1     Pending             0          0s
nginx-statefulset-0   0/1     ContainerCreating   0          0s
nginx-statefulset-0   0/1     ContainerCreating   0          1s
nginx-statefulset-0   1/1     Running             0          3s

  可以看到,按照倒序依次更新。待上一个Pod处于running状态后,才会启动下一个的更新。

 查看下升级后Pod实例的版本

[root@k8s-master yaml]# kubectl describe pod nginx-statefulset-1
...
Containers:
  nginx-stateful:
    Container ID:   docker://4caa6d45b6e6423d61bea97f93a680f7171c56462eaaa5e8af83bbe2d1917a6b
    Image:          nginx:latest

... 

可以看到,Pod已经升级到最新版本。

除了 RollingUpdate,还支持

  • OnDelete,删除升级策略,StatefulSet并不主动触发升级,而是要用户删除Pod,StatefulSet重建Pod时进行升级,实际是一种手动升级的方式。
  • partition,分区升级策略,用户指定一个序号,大于此序号的Pod实例全部升级,而小于此序号的Pod实例则依旧保持老版本不变,即使是这些Pod重建,依然使用老的版本。此策略一般用于按计划分步骤的升级,如金丝雀发布。

七,删除

      StatefulSet 同时支持级联和非级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除。使用级联删除时,StatefulSet 和它的 Pod 都会被删除。

   这里我们看下级联删除

 [root@k8s-master yaml]# kubectl delete sts nginx-statefulset
statefulset.apps "nginx-statefulset" deleted
[root@k8s-master yaml]# kubectl get sts
No resources found in default namespace.
[root@k8s-master yaml]# kubectl get pod -l app=nginx-stateful
No resources found in default namespace.

此时Pod已经被删除,再看下svc,pvc,pv的状态

[root@k8s-master yaml]# kubectl get svc
NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
stateful-svc                   ClusterIP      None            <none>          80/TCP         4d13h
[root@k8s-master yaml]# kubectl get pvc
NAME                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE

www-nginx-statefulset-0   Bound    a-pv                                       10Mi       RWX                           3d13h
www-nginx-statefulset-1   Bound    b-pv                                       10Mi       RWX                           3d13h
www-nginx-statefulset-2   Bound    c-pv                                       10Mi       RWX                           128m
www-nginx-statefulset-3   Bound    d-pv                                       10Mi       RWX                           127m
[root@k8s-master yaml]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                             STORAGECLASS   REASON   AGE
a-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-0                           3d13h
b-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-1                           3d13h
c-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-2                           27h
d-pv                                       10Mi       RWX            Recycle          Bound       default/www-nginx-statefulset-3                           27h

 与之相关联的Service,PVC,PV并没有删除,需要手动删除。 

非级联删除,相对于级联删除,指令上需要增加"--cascade=orphan"后缀参数。

 [root@k8s-master yaml]# kubectl delete sts nginx-statefulset  --cascade=orphan

 八、总结

     本章节介绍了有状态控制器StatefulSet,有状态实例有自己的主机标识,网络标识,存储标识的一致,实例之间不能相互替代,Pod实例重建时需要保持状态的一致,使得业务无感。          StatefulSet能根据一份容器镜像完成多个有状态的副本的状态,并自动创建PVC,并绑定PV,同时,与Deployment一样,StatefulSet控制器支持副本重建,扩缩容,滚动更新等。 

 附:

K8S初级入门系列之一-概述

K8S初级入门系列之二-集群搭建

K8S初级入门系列之三-Pod的基本概念和操作

K8S初级入门系列之四-Namespace/ConfigMap/Secret

K8S初级入门系列之五-Pod的高级特性

K8S初级入门系列之六-控制器(RC/RS/Deployment)

K8S初级入门系列之七-控制器(Job/CronJob/Daemonset)

K8S初级入门系列之八-网络

K8S初级入门系列之九-共享存储

K8S初级入门系列之十-控制器(StatefulSet)

K8S初级入门系列之十一-安全

K8S初级入门系列之十二-计算资源管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/791014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我的第一个flutter项目(Android Webview)

前言&#xff1a;flutter开发环境搭建Flutter的开发环境搭建-图解_☆七年的博客-CSDN博客 第一个flutter简单项目&#xff0c;内容是一个主界面&#xff0c;其中&#xff1a; 1.内容点击数字自增 2.跳转一个空页&#xff0c; 3.跳转一个WebView界面 其中涉及添加主键&#xf…

选择合适明星代言:确保品牌传播与销售成功的关键一步

在当今激烈的市场竞争中&#xff0c;企业需要不断探索新的营销策略来吸引消费者的关注和忠诚度。其中一种被广泛采用的方法是邀请明星代言产品或品牌。判断想请的明星与自己的产品是否相合适是十分重要的步骤&#xff0c;这关系到代言活动的成功与否。以下是一些方法可以帮助你…

DuckDB全面挑战SQLite

概要 当我们想要在具有嵌入式数据库的本地环境中工作时&#xff0c;我们倾向于默认使用 SQLite。虽然大多数情况下这都很好&#xff0c;但这就像骑自行车去 100 公里之外&#xff1a;可能不是最好的选择。 这篇文章中将讨论以下要点&#xff1a; • DuckDB 简介&#xff1a;它…

企业邮箱默认发信账户用途说明及设置方法

有的时候&#xff0c;企业有多个子公司&#xff0c;或者对内和对外需要用不同的邮箱地址&#xff0c;或者发给不同的人需要用不同的邮箱地址&#xff0c;这个时候企业或用户一般会设置别名邮箱用来区分。 那么问题来了&#xff0c;这么多邮箱账号&#xff0c;我发信的时候默认…

大模型的淘金时代,HPE给出了一份智能经济“奇点”攻略

进入2023年&#xff0c;ChatGPT引发了一个新的AI时代——大模型时代。陆奇说&#xff1a;“我已经跟不上大模型时代的狂飙速度了&#xff01;”大模型引发了AI产业整体升级换代&#xff0c;各种大模型层出不穷&#xff0c;科技公司纷纷入局&#xff0c;AI创业公司再次雨后春笋般…

【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(持久化功能分析)

探究Redis服务启动的过程机制的技术原理和流程分析的指南&#xff08;持久化功能分析&#xff09; Redis提供的持久化机制Redis持久化如何工作Redis持久化的故障分析持久化频率操作分析数据库多久调用一次write&#xff0c;将数据写入内核缓冲区&#xff1f;内核多久将系统缓冲…

算法空间复杂度详解

如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力之源&#xff0c;让我们一起加油&#xff0c;一起奔跑&#xff0c;让我们顶峰相见&#xff01;&#xff01;&#xff01; 前言 避免在处理大规模问题时出现效率低下&#xff0c;耗费较多…

STM32 Flash学习(一)

STM32 FLASH简介 不同型号的STM32&#xff0c;其Flash容量也不同。 MiniSTM32开发板选择的STM32F103RCT6的FLASH容量为256K字节&#xff0c;属于大容量产品。 STM32的闪存模块由&#xff1a;主存储器、信息块和闪存存储器接口寄存器等3部分组成。 主存储器&#xff0c;该部分…

服务器被爬虫恶意攻击怎么办?

在有预算的情况可以采购第三方服务防火墙&#xff0c;没钱就使用开源的WAF进行防护。 # WAF防火墙的基本防护原理 WAF&#xff08;Web 应用防火墙&#xff09;可以使用多种技术来防止恶意爬虫攻击&#xff0c;例如&#xff1a; 1. 黑名单&#xff1a;WAF 可以使用黑名单技术来…

会点C++还需要再学Python吗?

提到的C、数据结构与算法、操作系统、计算机网络和数据库技术等确实是计算机科学中非常重要的基础知识领域&#xff0c;对于软件开发和计算机工程师来说&#xff0c;它们是必备的核心知识。掌握这些知识对于开发高性能、可靠和安全的应用程序非常重要。Python作为一种脚本语言&…

Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台免费搭建

Java版知识付费-轻松拥有知识付费平台 多种直播形式&#xff0c;全面满足直播场景需求 公开课、小班课、独立直播间等类型&#xff0c;满足讲师个性化直播场景需求&#xff1b;低延迟、双向视频&#xff0c;亲密互动&#xff0c;无论是互动、答疑&#xff0c;还是打赏、带货、…

【C++初阶】介绍stack_queue及OJ题

介绍stack_queue及OJ题 前言一、简单了解1、stack2、queue 二、OJ题&#xff08;前三个栈&#xff0c;第四、五个队列&#xff09;1、最小栈&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;解题思路&#xff08;3&#xff09;解题代码 2、栈的压入、弹出序列&…

【C#】并行编程实战:异步流

本来这章该讲的是 ASP .NET Core 中的 IIS 和 Kestrel &#xff0c;但是我看了下这个是给服务器用的。而我只是个 Unity 客户端程序&#xff0c;对于服务器的了解趋近于零。 鉴于我对服务器知识和需求的匮乏&#xff0c;这里就不讲原书&#xff08;大部分&#xff09;内容了。本…

基于RPA的自动化流程治理方案探索及应用实践

编者荐语&#xff1a; 随着企业数字化转型进程加快&#xff0c;信息系统大量上线&#xff0c;但流程运营管理问题逐渐显现出来。为提升企业流程运营能力&#xff0c;亚信科技联合某省运营商推出智能化流程治理运营模式&#xff0c;尝试基于RPA&#xff08;机器人流程自动化&am…

IRIS搭建docker

之前把web实现了docker&#xff0c;开发或测试环境可能需要开发自己搭数据库&#xff0c;为了方便使用&#xff0c;把数据库也做一个docker。 由于原生的CentOS我还有改yum仓库&#xff0c;所以这次从之前lis搞的改好yum的镜像开始&#xff08;从改好yum的lisnew的镜像创建lis…

SaaS到底是什么,如何做?这份笔记讲明白了

阅读本篇文章&#xff0c;您将可以了解&#xff1a;1、什么是SaaS&#xff1b;2、SaaS的商业模式&#xff1b;3、SaaS的技术架构&#xff1b;4、国内比较好的SaaS平台。 一、什么是SaaS SaaS即软件即服务&#xff08;Software as a Service&#xff09;&#xff0c;是一种通过…

【数据结构】AVL树/红黑树

目录 1.AVL树&#xff08;高度平衡二叉搜索树&#xff09; 10.1.基本概念 10.2.实现 10.2.1.AVL树节点的定义 10.2.2.AVL树的插入 10.2.3.AVL树的旋转 1.新节点插入较高左子树的左侧---左左&#xff1a;右单旋 2.新节点插入较高右子树的右侧---右右&#xff1a;左单旋 3.新节点…

Python Flask构建微信小程序订餐系统 (十二)

🔥 创建切换商品分类状态的JS文件 🔥 ; var food_act_ops={init:function(){this.eventBind();},eventBind:function(){//表示作用域var that = this;$(".wrap_search select[name=status]").change(function(){$(".wrap_search").submit();});$(&qu…

对ai绘画二次元生成器你有多少了解?

在一个小镇上&#xff0c;有一位年轻的艺术家名叫艾米莉。她是个富有创意的女孩&#xff0c;总是追求着新奇和美妙的艺术体验。然而&#xff0c;她最近遇到了一些创作上的障碍&#xff0c;感觉自己的绘画已经陷入了瓶颈。在艾米莉寻找灵感的过程中&#xff0c;她听说了神秘的ai…

SQL语句(三十二)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、SQL语句类型 二、数据库操作 ​三、数据表操作 1. 数据类型 2. 查看 3. 创建 4. 删除 5. 更改 5.1 表 5.2 列 四、数据操作 4.1 增 4.2 删 4.3 改 4.4 查…