范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

开发和运维对K8S中的应用都做了什么?

  在应用的整个生命周期里,开发和运维都和它密不可分。一个塑造它,一个保养它。
  如果应用需要部署到K8S中,开发和运维在其中都做了什么呢?
  开发侧
  从开发侧来说,我们的应用应该具备以下能力:
  具有健康检测接口
  具有优雅退出能力
  具有metrics接口
  能够接入链路追踪系统
  日志输出标准统一
  定义健康检测接口
  健康检测接口用于检测应用的健康状态,在K8S中,使用Readiness和Liveness分别来探测应用是否就绪和是否存活,如果未就绪或者未存活,K8S会采取相应的措施来确保应用可用。
  如果我们应用未定义好相应的健康检测接口,K8S就无法判断应用是否正常可用,整个应用对我们来说就是黑匣子,也就谈不上应用稳定性了。
  定义一个简单的健康检测接口如下:package router
  import (
  "github.com/gin-gonic/gin"
  v1 "go-hello-world/app/http/controllers/v1"
  )
  funcSetupRouter(router *gin.Engine) {
  ruc := new(v1.RootController)
  router.GET("/", ruc.Root)
  huc := new(v1.HealthController)
  router.GET("/health", huc.HealthCheck)
  }
  package v1
  import (
  "github.com/gin-gonic/gin"
  "go-hello-world/app/http/controllers"
  "go-hello-world/pkg/response"
  "net/http"
  )
  type HealthController struct {
  controllers.BaseController
  }
  func(h *HealthController)HealthCheck(c *gin.Context) {
  response.WriteResponse(c, http.StatusOK, nil, gin.H{
  "result": "健康检测页面",
  "status": "OK",
  })
  }
  如上我们定义了health接口,当应用启动后,只需要探测这个接口,如果返回OK,表示应用是正常的。
  当然,上面的接口是非常简单的,在实际情况下,应用本身也许还依赖起来应用,比如redis,mysql,mq等,如果它们异常,应用是不是异常的呢?那我们的应用健康检测需不需要检测其他应用的健康状态呢?
  既然我们定义好了健康检测接口,那我们的YAML模板就可以增加健康检测功能,如下:readinessProbe:
  httpGet:
  path: /health
  port: http
  timeoutSeconds: 3
  initialDelaySeconds: 20
  livenessProbe:
  httpGet:
  path: /health
  port: http
  timeoutSeconds: 3
  initialDelaySeconds: 30
  定义优雅下线功能
  应用发版是常规不能再常规的操作,通常情况下都是滚动更新的方式上线,也就是先起一个新应用,再删一个老应用。
  如果这时候老应用有部分的流量,突然把老应用的进程杀了,这部分流量就无法得到正确的处理,部分用户也会因此受到影响。
  怎么才会不受影响呢?
  假如我们在停止应用之前先告诉网关或者注册中心,等对方把我们应用摘除后再下线,这样就不会有任何流量受到影响了。
  在K8S中,当我们要删除Pod的时候,Pod会变成Terminating状态,kubelet看到Pod的状态如果为Terminating,就会开始执行关闭Pod的流程,给Pod发SIGTERM信号,如果达到宽限期Pod还未结束就给Pod发SIGKILL信号,从Endpoints中摘除Pod等。
  从上面可知,Pod在停止之前会收到SIG信号,如果应用本身没有处理这些信号的能力,那应用如果知道什么时候该结束呢?
  下面简单定义一个处理SIG信号的功能。package shutdown
  import (
  "context"
  "fmt"
  "net/http"
  "os"
  "os/signal"
  "time"
  )
  // 优雅退出
  type Shutdown struct {
  ch chan os.Signal
  timeout time.Duration
  }
  func New(t time.Duration) *Shutdown {
  return &Shutdown{
  ch: make(chan os.Signal),
  timeout: t,
  }
  }
  func(s *Shutdown)Add(signals ...os.Signal) {
  signal.Notify(s.ch, signals...)
  }
  func(s *Shutdown)Start(server *http.Server) {
  <-s.ch
  fmt.Println("start exist......")
  ctx, cannel := context.WithTimeout(context.Background, s.timeout*time.Second)
  defer cannel
  if err := server.Shutdown(ctx); err != nil {
  fmt.Println("Graceful exit failed. err: ", err)
  }
  fmt.Println("Graceful exit success.")
  }
  package main
  import (
  "github.com/gin-gonic/gin"
  "go-hello-world/pkg/shutdown"
  "go-hello-world/router"
  "log"
  "net/http"
  "syscall"
  "time"
  )
  funcmain {
  r := gin.New
  // 注册路由
  router.SetupRouter(r)
  server := &http.Server{
  Addr: ":8080",
  Handler: r,
  }
  // 运行服务
  gofunc {
  err := server.ListenAndServe
  if err != nil && err != http.ErrServerClosed {
  log.Fatalf("server.ListenAndServe err: %v", err)
  }
  }
  // 优雅退出
  quit := shutdown.New(10)
  quit.Add(syscall.SIGINT, syscall.SIGTERM)
  quit.Start(server)
  }
  当接收到SIG信号的时候,就会调用Shutdown方法做应用退出处理。
  除此,还要结合K8S的PreStop Hook来定义结束前的钩子,如下:lifecycle:
  preStop:
  exec:
  command:
  - /bin/sh
  - "-c"
  - sleep 30
  如果使用注册中心,比如nacos,我们可以在PreStop Hook中先告诉nacos要下线,如下:lifecycle:
  preStop:
  exec:
  command:
  - /bin/sh
  - -c
  - "curl -X DELETE your_nacos_ip:8848/nacos/v1/ns/instance?serviceName=nacos.test.1&ip=${POD_IP}&port=8880&clusterName=DEFAULT" && sleep 30
  定义Metrics接口
  Metrics主要用来暴露应用指标,可以根据实际情况自定义指标,以便于监控工具Prometheus进行数据收集展示。
  有些语言有现成的exporter,比如java的jmx_exporter,没有的就需要自己在应用中集成。
  比如:package main
  import (
  "github.com/SkyAPM/go2sky"
  v3 "github.com/SkyAPM/go2sky-plugins/gin/v3"
  "github.com/SkyAPM/go2sky/reporter"
  "github.com/gin-gonic/gin"
  "github.com/prometheus/client_golang/prometheus/promhttp"
  "go-hello-world/pkg/shutdown"
  "go-hello-world/router"
  "log"
  "net/http"
  "syscall"
  "time"
  )
  var SKYWALKING_ENABLED = false
  funcmain {
  r := gin.New
  // 注册路由
  router.SetupRouter(r)
  server := &http.Server{
  Addr: ":8080",
  Handler: r,
  }
  // 启动metrics服务
  gofunc {
  http.Handle("/metrics", promhttp.Handler)
  if err := http.ListenAndServe(":9527", nil); err != nil {
  log.Printf("metrics port listen failed. err: %s", err)
  }
  }
  // 运行服务
  gofunc {
  err := server.ListenAndServe
  if err != nil && err != http.ErrServerClosed {
  log.Fatalf("server.ListenAndServe err: %v", err)
  }
  }
  // 优雅退出
  quit := shutdown.New(10)
  quit.Add(syscall.SIGINT, syscall.SIGTERM)
  quit.Start(server)
  }
  这种会暴露默认的Http指标,可以通过curl 127.0.0.1:9527/metrics获取指标。......
  # HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
  # TYPE promhttp_metric_handler_requests_total counter
  promhttp_metric_handler_requests_total{code="200"} 0
  promhttp_metric_handler_requests_total{code="500"} 0
  promhttp_metric_handler_requests_total{code="503"} 0
  如果需要自定义指标的话,只需按规则定义即可,如下:package metrics
  import (
  "github.com/prometheus/client_golang/prometheus"
  "net/http"
  "time"
  )
  var (
  // HttpserverRequestTotal 表示接收http请求总数
  HttpserverRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
  Name: "httpserver_request_total",
  Help: "The Total number of httpserver requests",
  },
  // 设置标签:请求方法和路径
  []string{"method", "endpoint"})
  HttpserverRequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
  Name: "httpserver_request_duration_seconds",
  Help: "httpserver request duration distribution",
  Buckets: []float64{0.1, 0.3, 0.5, 0.7, 0.9, 1},
  },
  []string{"method", "endpoint"})
  )
  // 注册监控指标
  funcinit {
  prometheus.MustRegister(HttpserverRequestTotal)
  prometheus.MustRegister(HttpserverRequestDuration)
  }
  func NewMetrics(router http.HandlerFunc) http.HandlerFunc {
  returnfunc(w http.ResponseWriter, r *http.Request) {
  start := time.Now
  router(w, r)
  duration := time.Since(start)
  // httpserverRequestTotal 记录
  HttpserverRequestTotal.With(prometheus.Labels{"method": r.Method, "endpoint": r.URL.Path}).Inc
  // httpserverRequestDuration 记录
  HttpserverRequestDuration.With(prometheus.Labels{"method": r.Method, "endpoint": r.URL.Path}).Observe(duration.Seconds)
  }
  }
  这样就定义了httpserver_request_total和httpserver_request_duration_seconds指标,引用过后就能在/metrics中看到对应的数据。
  定义好了指标,下面就是收集了。既可以通过自定义收集规则收集,也可以通过自动发现的方式收集,为了方便,主要采用自动发现的方式。
  我们只需要在deployment的templates中定义好annotation,prometheeus就会自动添加采集目标,如下:apiVersion: apps/v1
  kind: Deployment
  metadata:
  labels:
  app: httpserver
  name: httpserver
  namespace: default
  spec:
  replicas: 2
  selector:
  matchLabels:
  app: httpserver
  template:
  metadata:
  annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "metrics"
  labels:
  app: httpserver
  spec:
  containers:
  image: baidjay/httpserver:ubuntu-v3-metrics
  imagePullPolicy: IfNotPresent
  lifecycle:
  preStop:
  exec:
  command:
  - /bin/sh
  - -c
  - sleep 15
  livenessProbe:
  failureThreshold: 3
  httpGet:
  path: /healthz
  port: http
  scheme: HTTP
  initialDelaySeconds: 30
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  name: httpserver
  ports:
  - containerPort: 8080
  name: http
  protocol: TCP
  - name: metrics
  protocol: TCP
  containerPort: 9527
  readinessProbe:
  failureThreshold: 3
  httpGet:
  path: /healthz
  port: http
  scheme: HTTP
  initialDelaySeconds: 20
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  定义Trace功能
  Trace用于跟踪,每个请求都会生成一个TraceID,这个ID会伴随请求的整个生命周期,我们也可以根据这个ID查询请求的整个链路情况。
  链路追踪,目前市面上有很多开源系统,比如Skywalking,Jeager,Zipkin等,它们各有各的特点,如下。
  PinpointZipkinJaegerSkywalkingOpenTracing兼容否是是是客户端支持语言javaphpjavagophp等javagophp等java odejsphp等存储hbaseesmysql内存等eskafka内存等esmysqlh2等传输协议支持thrifthttpmqudphttpgrpcUI丰富程度高低中中实现方式字节码注入拦截请求拦截请求字节码注入扩展性低高高中Trace查询不支持支持支持支持告警支持支持不支持不支持支持JVM监控支持不支持不支持支持性能损失高中中低
  我比较推荐使用Jaeger,它是CNCF的毕业项目,成长空间和云原生的系统架构兼容性比较好。
  不过,我这里采用的Skywalking。
  Skywalking有许多现成的客户端,比如Java、Python等,可以直接使用,它们都会自动埋点,但是对于Go来说就只有自己手动埋点了,需要我们自己去写代码。
  比如:package main
  import (
  "github.com/SkyAPM/go2sky"
  v3 "github.com/SkyAPM/go2sky-plugins/gin/v3"
  "github.com/SkyAPM/go2sky/reporter"
  "github.com/gin-gonic/gin"
  "github.com/prometheus/client_golang/prometheus/promhttp"
  "go-hello-world/pkg/shutdown"
  "go-hello-world/router"
  "log"
  "net/http"
  "syscall"
  "time"
  )
  var SKYWALKING_ENABLED = false
  funcmain {
  r := gin.New
  // 配置skywalking
  if SKYWALKING_ENABLED {
  rp, err := reporter.NewGRPCReporter("skywalking-oap:11800", reporter.WithCheckInterval(time.Second))
  if err != nil {
  log.Printf("create gosky reporter failed. err: %s", err)
  }
  defer rp.Close
  tracer, _ := go2sky.NewTracer("go-hello-world", go2sky.WithReporter(rp))
  r.Use(v3.Middleware(r, tracer))
  }
  // 注册路由
  router.SetupRouter(r)
  server := &http.Server{
  Addr: ":8080",
  Handler: r,
  }
  // 启动metrics服务
  gofunc {
  http.Handle("/metrics", promhttp.Handler)
  if err := http.ListenAndServe(":9527", nil); err != nil {
  log.Printf("metrics port listen failed. err: %s", err)
  }
  }
  // 运行服务
  gofunc {
  err := server.ListenAndServe
  if err != nil && err != http.ErrServerClosed {
  log.Fatalf("server.ListenAndServe err: %v", err)
  }
  }
  // 优雅退出
  quit := shutdown.New(10)
  quit.Add(syscall.SIGINT, syscall.SIGTERM)
  quit.Start(server)
  }
  定义reporter用于上报数据给Skywalking,这就是一个简单的集成Trace的例子。
  定义标准的日志
  应用的可观测性主要来源日志、监控、链路追踪,标准的日志有利于日志收集以及排查问题。
  原则上,不论是什么类型的日志输出,什么格式的日志内容,都能收集。但是为了方便友好,建议把日志输出到标准输出,这样收集更方便。
  我个人理解,在K8s中,完全没必要把日志输出到文件,浪费不说,没多大意义,因为所有的日志我们都会收集到日志系统,而输出到文件的日志也会随着应用发版而丢失,所以输出到文件的意义是什么呢?
  运维侧
  开发把系统开发完,就会交付给运维部署。为了保障应用的稳定性,运维在部署应用的时候应该考虑以下几点。
  应用尽可能保持无状态
  应用尽可能保持高可用
  应该具备优雅上线能力
  应该具备异常自愈能力
  可以使用HTTPS访问
  应用尽可能保持无状态
  K8S中可以部署有状态应用,也可以部署无状态应用。对于有状态应用,我其实很少部署到K8S中,大部分还是部署的无状态应用,至于为什么,用多了就晓得了。
  对于业务应用,强烈建议使其保持无状态,就算有需要持久化的东西,要么保存到数据库,要么保存到对象存储或者其他单独的文件系统中,不要挂载到应用Pod上。
  这样的好处是,应用和数据是分开的,应用可以随意启停、扩展、迁移等。
  应用尽可能的保持高可用
  保持高可用应该是每个运维人员的使命。
  在K8S中,我们应该怎么配置呢?(1)应用Pod应该是多副本
  (2)应用Pod之间做反亲和性,避免同一应用调度到同一台主机,如下。......
  spec:
  affinity:
  podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - labelSelector:
  matchExpressions:
  - key: app
  operator: In
  values: [ "httpserver" ]
  topologyKey: kubernetes.io/hostname
  ......
  (3) 为了避免应用因为节点维护等原因驱逐Pod,导致全部Pod被驱逐,特别配置了PodDisruptionBudget,保障应用至少有一个可用,如下。apiVersion: policy/v1beta1
  kind: PodDisruptionBudget
  metadata:
  name: httpserver
  spec:
  minAvailable: 1
  selector:
  matchLables:
  app: httpserver
  (4)如果某个节点因为一些原因需要驱逐一些Pod,为了避免重要应用被驱逐,应该给应用配置较高的QoS,如下:resources:
  limits:
  cpu: "1"
  memory: 2Gi
  requests:
  cpu: "1"
  memory: 2Gi
  应用具备优雅上线能力
  所谓优雅上线能力,就是要确保应用能够提供服务了,再接入外界流量,不能在还没完全启动的情况下就提供服务。
  在K8S中,应用在启动后会加入endpoints中,然后通过service接入流量,那在什么情况下才算启动成功呢?主要是通过K8S的ReadinessProbe来进行检测。这时候开发的健康检测接口就派上用场了,如下:...
  readinessProbe:
  failureThreshold: 3
  httpGet:
  path: /health
  port: http
  scheme: HTTP
  initialDelaySeconds: 20
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  ...
  所以我们K8S的YAML文件应该加上如上的配置。
  应该具备异常自愈能力
  所谓异常自愈,就是应用本身在出现Crash,或者应用Pod所在节点出现异常的情况,应用能够自动重启或者迁移。这时候就需要通过K8S的LivenessProbe来进行检测了,如下。......
  livenessProbe:
  failureThreshold: 3
  httpGet:
  path: /health
  port: http
  scheme: HTTP
  initialDelaySeconds: 30
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  ......
  当K8S的YAML清单加上如上配置过后,就会定时去探测应用是否正常,如果异常,就会触发重启的动作。如果是节点异常,K8S会对Pod进行重新调度。
  可以使用HTTPS进行访问
  应用通过HTTPS访问是比较常见的,企业级应用建议自己购买相应的SSL证书,然后进行配置即可。
  比如。# 创建证书secret
  kubectl create secret tls httpserver-tls-secret --cert=path/to/tls.cert --key=path/to/tls.key
  # 在ingress中引用
  ......
  spec:
  tls:
  hosts:
  - httpserver.coolops.cn
  secretName: httpserver-tls-secret
  rules:
  - host: httpserver.coolops.cn
  ......
  总结
  上面介绍了开发和运维对于应用上线应该做的工作,不全但够用。
  在不同的企业都有不同的尿性,但是作为运维,我们都要牢牢记住稳定永远是第一尿性。通过上面的梳理,我们的应用模板就整理如下:apiVersion: apps/v1
  kind: Deployment
  metadata:
  labels:
  app: httpserver
  name: httpserver
  namespace: default
  spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
  matchLabels:
  app: httpserver
  strategy:
  rollingUpdate:
  maxSurge: 25%
  maxUnavailable: 25%
  type: RollingUpdate
  template:
  metadata:
  annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "metrics"
  labels:
  app: httpserver
  spec:
  affinity:
  podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - labelSelector:
  matchExpressions:
  - key: app
  operator: In
  values: [ "httpserver" ]
  topologyKey: kubernetes.io/hostname
  containers:
  - env:
  - name: TZ
  value: Asia/Shanghai
  - name: POD_NAME
  valueFrom:
  fieldRef:
  apiVersion: v1
  fieldPath: metadata.name
  - name: POD_NAMESPACE
  valueFrom:
  fieldRef:
  apiVersion: v1
  fieldPath: metadata.namespace
  image: baidjay/httpserver:ubuntu-v3-metrics
  imagePullPolicy: IfNotPresent
  lifecycle:
  preStop:
  exec:
  command:
  - /bin/sh
  - -c
  - sleep 15
  livenessProbe:
  failureThreshold: 3
  httpGet:
  path: /healthz
  port: http
  scheme: HTTP
  initialDelaySeconds: 30
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  name: httpserver
  ports:
  - containerPort: 8080
  name: http
  protocol: TCP
  - name: metrics
  protocol: TCP
  containerPort: 9527
  readinessProbe:
  failureThreshold: 3
  httpGet:
  path: /healthz
  port: http
  scheme: HTTP
  initialDelaySeconds: 20
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 3
  resources:
  limits:
  cpu: "1"
  memory: 2Gi
  requests:
  cpu: "1"
  memory: 2Gi
  securityContext: {}
  terminationMessagePath: /dev/termination-log
  terminationMessagePolicy: File
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  schedulerName: default-scheduler
  ---
  apiVersion: v1
  kind: Service
  metadata:
  name: httpserver
  spec:
  ports:
  - name: http
  port: 8080
  protocol: TCP
  targetPort: http
  - name: metrics
  port: 9527
  protocol: TCP
  targetPort: metrics
  selector:
  app: httpserver
  sessionAffinity: None
  type: ClusterIP
  ---
  apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
  annotations:
  nginx.ingress.kubernetes.io/proxy-body-size: 100m
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
  nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
  nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
  nginx.ingress.kubernetes.io/service-weight: ""
  nginx.org/client-max-body-size: 100m
  name: httpserver-tls
  spec:
  tls:
  - hosts:
  - httpserver.coolops.cn
  secretName: httpserver-tls-secret
  rules:
  - host: httpserver.coolops.cn
  http:
  paths:
  - pathType: Prefix
  path: /
  backend:
  service:
  name: httpserver
  port:
  number: 8080
  ---
  apiVersion: policy/v1
  kind: PodDisruptionBudget
  metadata:
  name: httpserver
  spec:
  minAvailable: 1
  selector:
  matchLabels:
  app: httpserver
  为了凑字数,写了一大堆,大家凑合看,觉得有用就点个赞~~!
  最后,求关注。如果你还想看更多优质原创文章,欢迎关注我们的公众号「运维开发故事」。
  如果我的文章对你有所帮助,还请帮忙一下,你的支持会激励我输出更高质量的文章,非常感谢!
  你还可以把我的公众号设为「星标」,这样当公众号文章更新时,你会在第一时间收到推送消息,避免错过我的文章更新。

比特币的主导地位随着山寨币的反弹而回来,又一次下跌?比特币(BTC)在过去两天的市场主导地位急剧下降,因为最新的加密货币反弹变得更加倾向于山寨币。机构购买和对赌住的兴趣似乎引发了反弹。周六,BTC的市场主导地位为40。9,较周四的4行业动态TCL华星首座海外工厂出货三星国际形势影响下显示行情复杂财联社(深圳,记者王碧微)讯,近期,TCL科技(000100。SZ)子公司TCL华星宣布印度华星首批产品成功出货三星,为三星手机产品提供屏幕。TCL华星COO赵军在媒体会上向记者表日本将帮助车企避免智能网联汽车专利纠纷来源盖世汽车谭璇盖世汽车讯据外媒报道,日本政府将制定一份指导方针,帮助企业在开发智能网联汽车的过程中避免专利诉讼,这是企业在开发智能网联汽车时面临的主要风险之一。日本经济产业省的指新能源车是否会出现一次灾难性产能过剩?来源南方plus新能源的整车特别是乘用车将会出现一次灾难性的产能过剩!4月1日,潍柴集团董事长谭旭光说道。这句话不是危言耸听。截至2021年底,全国乘用车产能合计4089万辆,产能在造车的江湖上,华为的传说还能坚持多久?4月初公布交付成绩的问界M5,将成为华为造车的最大变数。国内造车圈有许多未解之谜。其中关于华为到底造不造车的话题,看似早有官方定论,但依然让人猜测不透。在最近的华为年报发布会上,华星越L雷神HiX油电混动版上市,中国汽车开始加速超车!24年前没人看好吉利汽车,24年后没人敢小瞧吉利汽车。从摸着石头过河到自主品牌头部车企,除了吉利坚韧不屈的顽强毅力,也离不开越来越出色的产品实力。随着CMA架构导入,在三大件上完全中国系统社区微脑解决方案给城市居民稳稳地幸福来源三峡晚报社区作为城市治理的基础单元,说大不大说小也不小,一方面,它承载了城市居民的衣食住行,是人们日常生活最重要的空间另一方面,社区作为城市开展社会管理与服务工作的最后一公里,投影机有望打开这个200亿市场全新车载投影有望入市提到投影机,大家总能蹦出来一些关键词,例如护眼大画面便携看大片家庭娱乐等等。相比彩电,投影机更具可玩性,这是一个富有温度乐趣的新一代科技产品。特别是Z世代下的年轻消费者,投影机成为特斯拉站稳了,四辆新车冲你而来,颜值和实力能打,这次分胜负?由于燃油价格居高不下,使得当前国内市场新能源车关注度越来越高,特别是纯电动车产品,很多人愿意给次机会,所以需求高了,涉及其中的车企也多了。论起纯电动车影响力,国产车品牌多是在20万新能源汽车充电器烧了影响车子吗新能源汽车充电器烧了影响车子,电池满放满充很容易造成电池过放或过充。而过放过充不仅会使电池容量变小,缩短电池使用寿命,而且还可能造成电池燃烧。因此,从这个角度出发,经常以浅放浅充的俄罗斯军用无人机里全是欧美半导体大厂的芯片,这说明了什么?2014年因为克里米亚问题,俄罗斯就已经被制裁了,但2016年俄军使用的无人机上还是能找到来源不明的美国芯片,专家直言这些来源不明的芯片,各生产厂家其实已经具备识别技术,只待公私部
吐血整理,2万元内98寸电视随着技术的发展,大尺寸的电视价格越来越亲民,跟几年前的好几万甚至十几万相比现在的大屏电视价格越来越合理,而且体验过大尺寸的人就明白电视能买大就尽量买大的道理。那么先看下我在各家收集用了两年的iPhone,这些缺点真的让人抓狂,你有体会吗?iPhone是无可争议的手机标杆,这个结论应该没啥问题,主要体现在1独一档的A系列处理器,领先安卓两年的性能。2手机中最精致的做工最严格的品控。3领先的线性马达交互体验顶级的立体声到底装几个净水器才叫全屋净水?中国家庭净水市场发展了二十多年,对于许多人而言,净水器早已不是什么陌生新奇的事物。不管是老房翻新,还是新房装修,有越来越多的家庭消费者会把净水器的安装问题考虑进来。那么,该选择一套诺基亚携手鸿蒙,重新回归中高端智能手机市场,算是大招吗?如果诺基亚用鸿蒙,那下一步买诺基亚,诺基亚和蔡司镜头,确实是不错大招算不上,会吸引一部分诺基亚铁粉,尤其是30岁以上的人。诺基亚离开市场10年左右了,现在20岁的人当年才10岁,他耳朵流血了还可以继续使用助听器吗?什么样式的助听器?如果是耳内机,就不建议使用,因为如果还在流血会浸湿机芯,影响使用效果,如果是耳背机,又离不开助听器的帮扶,只要不刺激伤口可以戴,但最好还是等伤口好了再戴,也避免戴你们见过无论买多贵的手机都不贴膜不带套的人吗?买三星手机不用贴膜,我用过三星S6。8。9三个手机,没贴过膜,因为三星手机的屏幕是硬度极高的玻璃。只是塑料屏幕还是要贴膜的,不然准花。三星S7,S9,S105G,从不贴膜带壳。我就如何实现小程序变现功能?玩车教授最近分享了一个案例值得一看。小程序和既有业务结合,往往会迸发出巨大的能量。作为汽车领域的头部大号,玩车教授在小程序推出的第一时间跟进。从工具产品运营趋势三个层面,将这微信这message。py创建列表nameslist把天才放到列表中steve燕子nameslist。append(steve)david叶子nameslist。append(david)john狗儿nam华为mate20pro屏幕指纹版玻璃摔碎了,显示屏一切正常,换一下需要多少钱?如果能够正常显示,说明内屏是好的,内屏是显示屏,外屏是触摸屏,但是现在的屏幕技术,为了让显示效果更好,屏幕更薄,内外屏几乎是无缝贴合,所以几乎都是一起换的。官网才不管内外屏有问题呢再不用手洗拖布,彻底解放双手,云鲸扫地机器人扫拖一体机J2体验如今越来越多的家庭开始入手像扫地机器人扫拖一体机器人,洗地机器人等产品。随着科技的发展,这类产品的变化也随着单一功能,变得越来越智能化,比如,以往使用的扫地机器人只能做打扫的工作,小狗T12StormRinse测评篇五长扁吸软羽刷二合一宽嘴刷体验前面4期,我已经针对小狗T12StormRinse的旋转擦地刷双滚电动地刷电动除螨刷。这款产品主打的是擦地吸尘,旋转擦地刷可以同时实现吸尘和擦地同步完成,这在吸尘器上是比较少见的,