KEDA:基于事件驱动扩展K8S应用的深度实践

news2024/11/25 2:55:44

377dda26e68cee887345c2110dfbe896.gif

新钛云服已累计为您分享773篇技术干货

4dd631e1467c21d60d9a898c73bc07f6.gif

396808c889f14d4d208f1ca3a2528a74.png

为什么我们要自动扩展应用程序?

作为 SRE,需要保证应用弹性和高可用性。因此,自动缩放是我们需要的必须功能。通过自动缩放,我们能确保工作负载能够高效的地处理业务流量。

在本文中,我们将详细描述如何使用 KEDA 以事件驱动的方式自动扩展 Kubernetes 应用程序。

什么是KEDA?

KEDA 是一个轻量级的开源 Kubernetes 事件驱动的自动缩放器,DevOps、SRE 和 Ops 团队使用它来根据外部事件或触发器水平扩展 Pod。KEDA 有助于扩展本机 Kubernetes 自动缩放解决方案的功能,这些解决方案依赖于标准资源指标,如 CPU 或内存。我们可以将 KEDA 部署到 Kubernetes 集群中,并使用自定义资源定义 (CRD) 管理 Pod 的扩展。

KEDA 基于 Kubernetes HPA 构建,根据来自 AWS SQS、Kafka、RabbitMQ 等事件源的信息扩展 Pod。这些事件源使用缩放程序进行监视,缩放程序根据为其设置的规则激活或停用部署。KEDA 缩放器还可以为特定事件源提供自定义指标,帮助 DevOps 团队观察与其相关的指标

我们唯一要做的就是通过选择要用于自动扩展应用程序的缩放器以及一些参数来配置 ScaledObject (KEDA CRD),KEDA 将完成剩下的工作:

  • 监视事件源

  • 创建和管理 HPA 生命周期

截至目前,有 62 个内置缩放器和 4 个外部缩放器可用。 KEDA 之所以好,是因为使用轻量级组件以及原生 Kubernetes 组件,例如 HorizontalPodAutoscaler ,更重要的是“即插即用”。

部署KEDA

那么,部署 KEDA 的最简单方法是使用官方 Helm,安装如下所示:

helm repo add kedacore 
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace

‍‍‍

⚠️ 如果使用 ArgoCD 部署 KEDA,您可能会遇到有关 CRD 注释长度的问题。我们可以使用选项 ServerSideApply=true 来解决 template.sped.syncPolicy.syncOptions 。此外,还可以在 helm中设置参数,从而 禁用 CRD 部署,当然,这种情况下也必须找到另一种方法来部署 KEDA CRD。

基于本机Cron Scaler自动缩放Web应用

下面让我们深度实践一下KEDA!

部署我们的Web应用

对于demo,将使用一个建的 Golang Web 应用程序。使用以下清单部署:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: go-helloworld
  name: go-helloworld
spec:
  selector:
    matchLabels:
      app: go-helloworld
  template:
    metadata:
      labels:
        app: go-helloworld
    spec:
      containers:
        - image: rg.fr-par.scw.cloud/novigrad/go-helloworld:0.1.0
          name: go-helloworld
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              memory: "128Mi"
              cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
  name: go-helloworld
spec:
  selector:
    app: go-helloworld
  ports:
    - protocol: TCP
      port: 8080
      name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
  name: go-helloworld
spec:
  rules:
  - host: helloworld.jourdain.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: go-helloworld
            port:
              number: 8080
  tls: # < placing a host in the TLS config will indicate a certificate should be created
  - hosts:
    - helloworld.jourdain.io
    secretName: go-helloworld-tls-cert

将KEDA配置为仅在工作时间自动扩展Web应用

如果,我们希望我们的应用程序仅在工作时间可用。至于为何有如此要求,下面是一些场景。

例如,在开发环境中,不一定需要保持应用程序24小时启动和运行。在云环境中,它可以为您节省大量成本,具体取决于用户实际环境的应用程序/计算实例的数量。 为了实现这一点,我们将使用KEDA的原生Cron scaler。由于 Cron scaler 支持 Linux 格式的 cron,它甚至允许我们在工作日扩展我们的应用程序 

要配置 Cron scaler,我们将按如下方式使用 [ScaledObject] CRD:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: go-helloworld
spec:
  scaleTargetRef:
    name: go-helloworld
  triggers:
  - type: cron
    metadata:
      timezone: Europe/Paris
      start: 00 08 * * 1-5
      end: 00 18 * * 1-5
      desiredReplicas: "2"

‍‍‍

⚠️ ScaledObject 必须与应用程序位于同一命名空间中!

让我们深入了解一下这个配置:

  • spec.scaleTargetRef 是 Kubernetes Deployment/StatefulSet 或其他自定义资源的引用

    • name (必填): Kubernetes 资源的名称

    • kind (可选):Kubernetes 资源的种类,默认值为 Deployment

  • spec.triggers 是用于激活目标资源缩放的触发器列表

    • type (必填):缩放器名称

    • metadata (必需):Cron 缩放器所需的配置参数,使用此配置,我的应用程序将在周一到周五的一周中每天的 08:00 到 18:00 之间启动并运行两个副本。

基于HTTP事件自动缩放Web应用

(使用 KEDA HTTP 外部缩放器)

借助所有 KEDA 的缩放器,我们可以通过多种方式自动扩展 Web 应用程序,例如,基于 AMQP 队列中的消息进行缩放应用。

目前,我们了解了 KEDA 的工作原理。我下面们将探讨 KEDA 如何通过基于 HTTP 事件自动扩展应用程序来帮助我们处理流量高峰。为此,我们有两个选择:

  • 使用Prometheus scaler

  • 使用 KEDA HTTP 外部缩放器,它的工作方式类似于附加组件,由于演示集群上没有安装 Prometheus,因此我们将使用 KEDA HTTP 外部缩放器。

💡 KEDA HTTP插件目前处于测试阶段。它主要由KEDA团队维护。

解决方案概述

KEDA HTTP scaler是构建在 KEDA 核心之上的附加组件,它拥有自己的组件:operator, scaler和 interceptor。如果你想进一步了解它们的作用,请阅读官方文档。总之,为了帮助大家更好地理解它的工作原理,下面提供一个小案例:

82c61825708b5208d06c1fd5ac784eab.png

‍‍‍

安装KEDA HTTP附加组件

由于这个缩放器不是内置的,我们必须手工安装。根据官方文档的说明,我们可以使用 Helm 来进行安装:

helm install http-add-on kedacore/keda-add-ons-http --namespace keda

‍‍‍

如果安装顺利,我们应该会看到以下pod:

❯ k get pods -l app=keda-add-ons-http -o name
pod/keda-add-ons-http-controller-manager-5c8d895cff-7jsl8
pod/keda-add-ons-http-external-scaler-57889786cf-r45lj
pod/keda-add-ons-http-interceptor-5bf6756df9-wwff8
pod/keda-add-ons-http-interceptor-5bf6756df9-x8l58
pod/keda-add-ons-http-interceptor-5bf6756df9-zxvw

‍‍‍

为我们的Web应用配置'HTTPScaledObject'

正如之前所说,KEDA HTTP 附加组件自带组件,包括操作符,这也意味着它自带 CRD。HTTPScaledObject 是由 KEDA HTTP 附加组件管理的 CRD。这就是我们在这里需要配置的。让我们为 Web 应用程序创建 HTTPScaledObject 资源: 

⚠️ HTTPScaleObject 必须在与 Web 应用相同的命名空间中创建资源!

kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
    name: go-helloworld
spec:
    host: "helloworld.jourdain.io"
    targetPendingRequests: 10
    scaledownPeriod: 300
    scaleTargetRef:
        deployment: go-helloworld
        service: go-helloworld
        port: 8080
    replicas:
        min: 0
        max: 10

‍‍‍

在这里,我们已经配置了我们的 HTTPScaledObject 应用程序,以便将我们的应用程序 Deployment 从 0 个副本扩展到 10 个副本。因为,如果拦截器上有 10 个请求处于挂起状态(应用程序尚未接收的请求),则 KEDA 将添加一个 pod。

调整我们的Web应用程序的service和ingress

仔细观察一下上面的图,可以看到我们的 Web 应用程序 ingress 需要引用 KEDA HTTP 附加组件的拦截器服务,而不是 Web 应用程序的拦截器服务。由于 ingress 无法引用另一个命名空间中的服务,因此我们将在与 Web 应用相同的命名空间 external 中创建类型服务,该服务引用来自 keda 命名空间的拦截器服务:

kind: Service
apiVersion: v1
metadata:
  name: keda-add-ons-http-interceptor-proxy
spec:
  type: ExternalName
  externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local

‍‍‍

现在,我们需要重新配置 Web 应用的入口,使其引用新创建的服务:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
  name: go-helloworld
spec:
  rules:
  - host: helloworld.jourdain.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: keda-add-ons-http-interceptor-proxy
            port:
              number: 8080
  tls: # < placing a host in the TLS config will indicate a certificate should be created
  - hosts:
    - helloworld.jourdain.io
    secretName: go-helloworld-tls-cert

‍‍‍

⚠️ 需要输入新服务的名称,但请注意端口,该端口也被拦截器的服务所取代

让我们测试一下

为了保证我们的配置正常,本处将使用 [k6]工具 ,这是一个负载测试工具。如果想了解有关k6的更多信息,以下是Padok博客中的一些介绍:

  • [How to do distributed load testing using K6 & Kubernetes?] (https://www.padok.fr/en/blog/k6-load-testing)

  • [k6 description from the Padok tech radar] (https://www.padok.fr/en/tech-radar-resilient?category=resilient&rank=6)

下面本次使用的 k6 脚本,后续将使用它进行测试:

import { check } from 'k6';
import http from 'k6/http';


export const options = {
  scenarios: {
    constant_request_rate: {
      executor: 'constant-arrival-rate',
      rate: 100,
      timeUnit: '1s', // 100 iterations per second, i.e. 100 RPS
      duration: '30s',
      preAllocatedVUs: 50, // how large the initial pool of VUs would be
      maxVUs: 50, // if the preAllocatedVUs are not enough, we can initialize more
    },
  },
};


export function test(params) {
  const res = http.get('');
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
}


export default function () {
  test();
}

‍‍‍

首先,让我们看看 100 个 RPS 会发生什么:

❯ k6 run k6/script.js


          /\\      |‾‾| /‾‾/   /‾‾/
     /\\  /  \\     |  |/  /   /  /
    /  \\/    \\    |     (   /   ‾‾\\
   /          \\   |  |\\  \\ |  (‾)  |
  / __________ \\  |__| \\__\\ \\_____/ .io


  execution: local
     script: k6/script.js
     output: -


  scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop):
           * constant_request_rate: 100.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s)




     ✓ is status 200


     checks.........................: 100.00% ✓ 3001      ✗ 0
     data_received..................: 845 kB  28 kB/s
     data_sent......................: 134 kB  4.5 kB/s
     http_req_blocked...............: avg=792.54µs min=0s     med=1µs     max=137.85ms p(90)=2µs     p(95)=2µs
     http_req_connecting............: avg=136.6µs  min=0s     med=0s      max=17.67ms  p(90)=0s      p(95)=0s
     http_req_duration..............: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
       { expected_response:true }...: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
     http_req_failed................: 0.00%   ✓ 0         ✗ 3001
     http_req_receiving.............: avg=89.68µs  min=8µs    med=64µs    max=6.35ms   p(90)=112µs   p(95)=134µs
     http_req_sending...............: avg=152.31µs min=14µs   med=137µs   max=2.57ms   p(90)=274µs   p(95)=313µs
     http_req_tls_handshaking.......: avg=587.62µs min=0s     med=0s      max=74.46ms  p(90)=0s      p(95)=0s
     http_req_waiting...............: avg=11.14ms  min=7.62ms med=10.48ms max=100.92ms p(90)=12.47ms p(95)=13.96ms
     http_reqs......................: 3001    99.983105/s
     iteration_duration.............: avg=12.37ms  min=7.73ms med=10.88ms max=194.89ms p(90)=13.07ms p(95)=14.99ms
     iterations.....................: 3001    99.983105/s
     vus............................: 1       min=1       max=1
     vus_max........................: 50      min=50      max=50




running (0m30.0s), 00/50 VUs, 3001 complete and 0 interrupted iterations
constant_request_rate ✓ [======================================] 00/50 VUs  30s  100.00 iters/s

‍‍‍

💡 如果您想实时查看拦截器队列中有多少请求,可以在两个终端窗格中启动以下命令:

❯ kubectl proxy


Starting to serve on 127.0.0.1:8001

‍‍‍

以及:

❯ watch -n '1' curl --silent localhost:8001/api/v1/namespaces/keda/services/keda-add-ons-http-interceptor-admin:9090/proxy/queue


{"default/go-helloworld":0}

‍‍‍

在 100 RPS 测试中,应用程序没有纵向扩展,因为拦截器队列中的挂起请求数不超过 1。提醒一下,我们配置为 targetPendingRequests 10 。所以一切都很正常 😁

下面让我们将 RPS x10 ,看看会发生什么:

❯ k6 run k6/script.js


          /\\      |‾‾| /‾‾/   /‾‾/
     /\\  /  \\     |  |/  /   /  /
    /  \\/    \\    |     (   /   ‾‾\\
   /          \\   |  |\\  \\ |  (‾)  |
  / __________ \\  |__| \\__\\ \\_____/ .io


  execution: local
     script: k6/script.js
     output: -


  scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop):
           * constant_request_rate: 1000.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s)


     ✗ is status 200
      ↳  99% — ✓ 11642 / ✗ 2


     checks.........................: 99.98% ✓ 11642      ✗ 2
     data_received..................: 2.6 MB 86 kB/s
     data_sent......................: 446 kB 15 kB/s
     dropped_iterations.............: 18356  611.028519/s
     http_req_blocked...............: avg=1.07ms   min=0s     med=0s      max=408.06ms p(90)=1µs      p(95)=1µs
     http_req_connecting............: avg=43.12µs  min=0s     med=0s      max=11.05ms  p(90)=0s       p(95)=0s
     http_req_duration..............: avg=120.09ms min=8.14ms med=74.77ms max=6.87s    p(90)=189.49ms p(95)=250.21ms
       { expected_response:true }...: avg=120.01ms min=8.14ms med=74.76ms max=6.87s    p(90)=189.41ms p(95)=249.97ms
     http_req_failed................: 0.01%  ✓ 2          ✗ 11642
     http_req_receiving.............: avg=377.61µs min=5µs    med=32µs    max=27.32ms  p(90)=758.1µs  p(95)=2.49ms
     http_req_sending...............: avg=61.57µs  min=9µs    med=45µs    max=9.99ms   p(90)=102µs    p(95)=141µs
     http_req_tls_handshaking.......: avg=626.79µs min=0s     med=0s      max=297.82ms p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=119.65ms min=7.95ms med=74.32ms max=6.87s    p(90)=188.95ms p(95)=249.76ms
     http_reqs......................: 11644  387.60166/s
     iteration_duration.............: avg=121.26ms min=8.32ms med=74.87ms max=7.07s    p(90)=189.62ms p(95)=250.28ms
     iterations.....................: 11644  387.60166/s
     vus............................: 44     min=25       max=50
     vus_max........................: 50     min=50       max=50




running (0m30.0s), 00/50 VUs, 11644 complete and 0 interrupted iterations
constant_request_rate ✓ [======================================] 00/50 VUs  30s  1000.00 iters/s

‍‍‍

结果还不错,我们有两个请求 KO ,这是因为应用程序冷启动(从 0 开始),每个请求等待的时间不超过 1/2 秒。

以下是部署历史记录:

❯ k get deployments.apps -w
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
go-helloworld   0/0     0            0           36m
go-helloworld   0/1     0            0           36m
go-helloworld   1/1     1            1           36m
go-helloworld   1/4     1            1           36m
go-helloworld   2/4     4            2           36m
go-helloworld   3/4     4            3           36m
go-helloworld   4/4     4            4           36m
go-helloworld   4/5     4            4           37m
go-helloworld   5/5     5            5           37m

‍‍‍

应用程序从 0 个副本扩展到 5 个副本;直到 Web 应用程序的挂起请求数少于 10。

缩放指令非常快,应用程序很快达到了 5 个副本。

以下是 100 RPS 和 1k RPS 测试之间 http_req_duration k6 指标的一些对比:

# 100 RPS
http_req_duration: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms


# 1k RPS
http_req_duration: avg=120.09ms min=8.14ms med=74.77ms max=6.87s    p(90)=189.49ms p(95)=250.21ms

根据我们的需求(SLO,SLA等),我们也许可以稍微调整一下 Web应用程序的 targetPendingRequestsHTTPScaledObject 参数 。

缩放到零!

通过本文介绍的两个案例,我们已经验证过了如何缩放到零的场景。但是,大家真的知道它是如何工作的吗?

由于 KEDA 根据事件自动缩放应用程序,因此从收到事件的那一刻起,KEDA 会将应用程序缩放到其最小副本。例如,如果我们以 HTTP 附加组件为例,KEDA 将在第一次收到请求时扩展到最小副本。

总结

KEDA 提供了一个类似于 FaaS 的事件感知扩展模型,在这种模型中,Kubernetes 部署可以基于需求和基于智能动态地从零扩展,而不会丢失数据和上下文。在业务请求量上来后,应用程序将进行自动化的扩容,当业务低谷的时候,则会自动的缩容。这可以在缓解很多生产环境下的手动扩/缩容操作,以保障用户的服务体验。

KEDA 还为 Kubernetes 带来了更多的事件源。随着未来更多触发器的加入,KEDA 有很大的潜力成为生产级 Kubernetes 部署的必需品,从而使应用程序自动缩放成为应用程序开发中的嵌入式组件。

    推荐阅读   

40bf375c73cfbcb84fc27fdb742c1427.png

f6847ea36b0b85fdc022277096950e1b.png

    推荐视频    

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

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

相关文章

[vmware]vmware虚拟机压缩空间清理空间

vmware中的ubuntu使用如果拷贝文件进去在删除&#xff0c;vmare镜像文件并不会减少日积月累会不断是的真实物理磁盘空间大幅度减少&#xff0c;比如我以前windows操作系统本来只有30GB最后居然占道硬盘200GB&#xff0c;清理方法有2种。 第一种&#xff1a;vmware界面操作 第二…

Dubbo篇---第二篇

系列文章目录 文章目录 系列文章目录一、注册中心挂了,consumer 还能不能调用 provider?二、怎么实现动态感知服务下线的呢?三、Dubbo 负载均衡策略?一、注册中心挂了,consumer 还能不能调用 provider? 可以。因为刚开始初始化的时候,consumer 会将需要的所有提供者的地…

多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉 计算机竞赛

文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习多目标跟踪 …

解决Ts中的error.stack报错property ‘stack‘ does not exist on type ‘unknown typescript

我用的Ts版本是5.x&#xff0c;所以在使用的时候出现了这个问题 解决方式&#xff1a; 将error先转一遍就好了 参考链接&#xff1a; 你真的会处理TS中的Error么 - 掘金 (juejin.cn) Announcing TypeScript 4.4 - TypeScript (microsoft.com)

黑群晖安装及使用小计

最近发现4t的盘装满了&#xff0c;最主要的照片也来到了4万张大关。所以怎么处理&#xff0c;怎么定位&#xff0c;怎么更方便的查找图片就变得很重要。 第一步我选择了picturama这个软件&#xff0c;只要指定图片文件夹&#xff0c;能够很快速 的浏览&#xff0c;并且带时间轴…

Zabbix结合Grafana统计日志网站访问量

Zabbix除了可以通过HTTP代理及WEB场景监控网站的响应结果、响应时间和传输速度等&#xff0c;也可以通过读取网站的后台日志&#xff0c;获取有用的统计信息。 下面我以Grafana为例&#xff0c;通过日志统计网站的访问量。 操作如下&#xff1a; 1、读取grafana日志 首先要基…

基于单片机的商场防盗防火系统设计

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统分析二、系统总设计2.1基于单片机的商场防火防盗系统的总体功能2.2系统的组成 三 软件设计4.1软件设计思路4.2软件的实现4.2.1主控模块实物 四、 结论五、 文章目录 概要 本课题设计一种商场防火防盗报警…

Webpack的入口(entry)和出口(output)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

C++笔记之std::pair

C笔记之std::pair code review!

java EE 进阶

java EE 主要是学框架(框架的使用,框架的原理) 框架可以说是实现了部分功能的半成品,还没装修的毛坯房,然后我们再自己打造成自己喜欢的成品 这里学习四个框架 : Spring ,Spring Boot, Spring MVC, Mybatis JavaEE 一定要多练习,才能学好 Maven 目前我们主要用的两个功能: …

堆叠注入 [GYCTF2020]Blacklist1

打开题目 判断注入点 输入1&#xff0c;页面回显 输入1 页面报错 输入 1 # 页面正常&#xff0c;说明是单引号的字符型注入 我们输入1; show databases; # 说明有6个数据库 1; show tables; # 说明有三个表 我们直接查看FlagHere的表结构 1;desc FlagHere&#xff1b;# 发…

selenium自动化测试入门 —— 定位frame和iframe中的元素对象

< frame> <iframe> 标签&#xff0c;浏览器会在标签中打开一个特定的页面窗口&#xff08;框架&#xff09;&#xff0c;它在本窗口中嵌套进入一个网页&#xff0c;当用selenium定位页面元素的时候会遇到定位不到frame框架内的元素的问题。 定位frame中的元素前我…

代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ 文章链接&#xff1a;完全背包理论基础 零钱兑换 II 组合总和 Ⅳ 视频链接&#xff1a;完全背包理论基础 零钱兑换 II 组合总和 Ⅳ 1. 完全背包理论基础 1.1 思路 完全背包和 01 背包的区别就是…

Find My手机保护壳|苹果Find My与手机保护壳结合,智能防丢,全球定位

随着科技水平的快速发展&#xff0c;科技美容这一行业做为新型产业新生而出。时尚IT品牌随着市场的多元化发展。针对手机品牌和功能的增加而呈多样化&#xff0c;将手机保护壳按质地分有PC壳&#xff0c;皮革 &#xff0c;硅胶&#xff0c;布料&#xff0c;硬塑&#xff0c;皮套…

0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发

文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 移动互联网时代的到来&#xff0c;给人们的生活带来了许多便捷和乐趣。随着用户的不断增多&#xff0c;其规模越来越大&#…

leetCode 494. 目标和 + 动态规划 + 记忆化搜索 + 递推 + 空间优化

关于本题我的往期文章&#xff1a; LeetCode 494.目标和 &#xff08;动态规划 性能优化&#xff09;二维数组 压缩成 一维数组_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://heheda.blog.csdn.net/article/details/133253822 给你一个非负整数数组 nums…

从lc114. 二叉树展开为链表到lc-LCR 155二叉搜索树转化为排序的双向链表

1 lc114. 二叉树展开为链表 1.1 描述 进阶&#xff1a;你可以使用原地算法&#xff08;O(1) 额外空间&#xff09;展开这棵树吗&#xff1f; 1.2 解法一&#xff1a; 先序遍历这棵树并且将节点加入到一个list中&#xff0c;随后按顺序将list中的每一个元素的left指针置换为…

如何保证消息只被消费一次

目录 前言 一、什么是幂等&#xff1f; 二、在生产过程中增加消息幂等性的保证 三、在消费过程中增加消息幂等性的保证 前言 消息一旦被重复消费&#xff0c;就会造成业务逻辑处理的错误。那么我们要如何避免消息的重复呢&#xff1f; 想要完全的避免消息重复的发生是很难…

全方位移动机器人 SolidWorks 转 URDF 并在 Rviz 中仿真

全方位移动机器人 SolidWorks 转 URDF 并在 Rviz 中仿真 参考 solidworks转URDF&#xff0c;并且在rviz中仿真 从solidworks导出URDF模型 Export a SolidWorks Assembly to URDF Solidworks模型导出urdf SolidWorks 模型简化 将整车除车轮部分另存为零件&#xff0c;作为一个…

MySQL连接时出现Host ‘::1‘ is not allowed to connect to this MySQL server

报错原因 之前想着要提高一下连接速度&#xff0c;所以在my.ini中加入了&#xff1a;skip-name-resolve&#xff0c;当时的数据库root账号设置的登录权限是%&#xff0c;因此没有出现连接错误&#xff0c;这次因为是新建数据库&#xff0c;root账号的登录权限默认是localhost&…