kube-ovn默认vpc

下面图是kube-ovn默认vpc的拓扑

默认vpc

kube-ovn安装完成后会自带一个默认vpc是ovn-cluster,并且会在这个默认vpc下创建ovn-default子网、join子网,

默认子网

ovn-default是ovn-cluster下的默认子网,在创建pod时没有指定子网时会使用这个默认子网的ip。ovn-default子网的cidr是10.16.0.0/16。可以在安装kube-ovn时通过修改install.sh脚本来进行指定。

join子网

join子网是默认vpc下用来管理node的子网,kube-ovn会在k8s的每个集群的节点上的br-int上创建一个ovn0网卡,用来实现默认vpc下pod与节点之间的互通,并且实现默认vpc下的网关功能。join子网的网段是100.64.0.0/16,可以通过修改install.sh来修改join子网的网段

通过配置ovn来模拟实现kube-ovn的默认vpc下分布式网关

物理环境

首先ovn构建vpc的网络拓扑

流程:

  1. 创建lr1逻辑路由器

  2. 创建lr1的lr1-ls1逻辑路由器端口

  3. 创建lr1的lr1-ls2逻辑路由器端口

  4. 创建ls1逻辑交换机

  5. 创建ls2逻辑交换机

  6. 创建ls1的ls1-lr1的逻辑交换机端口并与lr1相连

  7. 创建ls2的ls2-lr1的逻辑交换机端口并与lr1相连

  8. 给ls1创建两个端口

  9. 给ls2创建两个端口

  10. 在master节点上创建vm1、vm2,并连接到br-int上

  11. 在node1节点上创建vm1、vm2,并连接上br-int上

master节点
ovn-nbctl ls-add ls1
ovn-nbctl ls-add ls2
ovn-nbctl lr-add lr1ovn-nbctl lrp-add lr1 lr1-ls1 00:00:00:00:00:01 10.10.10.1/24ovn-nbctl lsp-add ls1 ls1-lr1
ovn-nbctl lsp-set-type ls1-lr1 router
ovn-nbctl lsp-set-addresses ls1-lr1 00:00:00:00:00:01
ovn-nbctl lsp-set-options ls1-lr1 router-port=lr1-ls1ovn-nbctl lrp-add lr1 lr1-ls2 00:00:00:00:00:02 10.10.20.1/24ovn-nbctl lsp-add ls2 ls2-lr1
ovn-nbctl lsp-set-type ls2-lr1 router
ovn-nbctl lsp-set-addresses ls2-lr1 00:00:00:00:00:02
ovn-nbctl lsp-set-options ls2-lr1 router-port=lr1-ls2ovn-nbctl lsp-add ls1 ls1-vm1
ovn-nbctl lsp-set-addresses ls1-vm1 "00:00:00:00:00:03 10.10.10.2"
ovn-nbctl lsp-set-port-security ls1-vm1 "00:00:00:00:00:03 10.10.10.2"ovn-nbctl lsp-add ls1 ls1-vm2
ovn-nbctl lsp-set-addresses ls1-vm2 "00:00:00:00:00:04 10.10.10.3"
ovn-nbctl lsp-set-port-security ls1-vm2 "00:00:00:00:00:04 10.10.10.3"ovn-nbctl lsp-add ls2 ls2-vm1
ovn-nbctl lsp-set-addresses ls2-vm1 "00:00:00:00:00:03 10.10.20.2"
ovn-nbctl lsp-set-port-security ls2-vm1 "00:00:00:00:00:03 10.10.20.2"ovn-nbctl lsp-add ls2 ls2-vm2
ovn-nbctl lsp-set-addresses ls2-vm2 "00:00:00:00:00:04 10.10.20.3"
ovn-nbctl lsp-set-port-security ls2-vm2 "00:00:00:00:00:04 10.10.20.3"ip netns add vm1
ovs-vsctl add-port br-int vm1 -- set interface vm1 type=internal
ip link set vm1 netns vm1
ip netns exec vm1 ip link set vm1 address 00:00:00:00:00:03
ip netns exec vm1 ip addr add 10.10.10.2/24 dev vm1
ip netns exec vm1 ip link set vm1 up
ip netns exec vm1 ip route add default via 10.10.10.1 dev vm1
ovs-vsctl set Interface vm1 external_ids:iface-id=ls1-vm1ip netns add vm2
ovs-vsctl add-port br-int vm2 -- set interface vm2 type=internal
ip link set vm2 netns vm2
ip netns exec vm2 ip link set vm2 address 00:00:00:00:00:04
ip netns exec vm2 ip addr add 10.10.10.3/24 dev vm2
ip netns exec vm2 ip link set vm2 up
ip netns exec vm2 ip route add default via 10.10.10.1 dev vm2
ovs-vsctl set Interface vm2 external_ids:iface-id=ls1-vm2master清除环境
ovs-vsctl del-port br-int vm1
ovs-vsctl del-port br-int vm2
ip netns del vm1
ip netns del vm2
ovn-nbctl ls-del ls1
ovn-nbctl ls-del ls2
ovn-nbctl lr-del lr1node1节点
ip netns add vm1
ovs-vsctl add-port br-int vm1 -- set interface vm1 type=internal
ip link set vm1 netns vm1
ip netns exec vm1 ip link set vm1 address 00:00:00:00:00:03
ip netns exec vm1 ip addr add 10.10.20.2/24 dev vm1
ip netns exec vm1 ip link set vm1 up
ip netns exec vm1 ip route add default via 10.10.20.1 dev vm1
ovs-vsctl set Interface vm1 external_ids:iface-id=ls2-vm1ip netns add vm2
ovs-vsctl add-port br-int vm2 -- set interface vm2 type=internal
ip link set vm2 netns vm2
ip netns exec vm2 ip link set vm2 address 00:00:00:00:00:04
ip netns exec vm2 ip addr add 10.10.20.3/24 dev vm2
ip netns exec vm2 ip link set vm2 up
ip netns exec vm2 ip route add default via 10.10.20.1 dev vm2
ovs-vsctl set Interface vm2 external_ids:iface-id=ls2-vm2node1清除
ovs-vsctl del-port br-int vm1
ovs-vsctl del-port br-int vm2
ip netns del vm1
ip netns del vm2

创建join子网

 
master节点
# 创建逻辑交换机jion
ovn-nbctl ls-add join# 关联逻辑路由器router0
ovn-nbctl lrp-add lr1 lr1-join 04:ac:10:ff:35:02 10.10.30.1/24
ovn-nbctl lsp-add join join-lr1 
ovn-nbctl lsp-set-type  join-lr1  router
ovn-nbctl lsp-set-addresses  join-lr1  04:ac:10:ff:35:02
ovn-nbctl lsp-set-options  join-lr1  router-port=lr1-joinovn-nbctl lsp-add join master
ovn-nbctl lsp-set-addresses  master    "04:ac:10:ff:35:04 10.10.30.2"ovn-nbctl lsp-add join node1
ovn-nbctl lsp-set-addresses  node1    "04:ac:10:ff:35:05 10.10.30.3"# 在master节点创建ovn0网卡
ovs-vsctl add-port br-int ovn0 -- set Interface ovn0 type=internal
ovs-vsctl set Interface ovn0  external_ids:iface-id=master
ifconfig ovn0 10.10.30.2  netmask 255.255.255.0
ifconfig ovn0 hw ether 04:ac:10:ff:35:04node1节点
# 在node01上
ovs-vsctl add-port br-int ovn0 -- set Interface ovn0 type=internal
ovs-vsctl set Interface ovn0  external_ids:iface-id=node1
ifconfig ovn0 10.10.30.3  netmask 255.255.255.0
ifconfig ovn0 hw ether 04:ac:10:ff:35:05

添加默认ovn的静态路由

master节点# 添加后容器内可以ping通宿主机,指定lr1的路由规则,指定ip从某个端口出去
ovn-nbctl   lr-route-add  lr1   0.0.0.0/0  10.10.30.1

目的是给默认vpc对应的逻辑路由器添加路由条目,将所有的报文的下一条指定为join子网的路由端口

在节点上添加路由表和snat规则

master节点
# 用来引流到ovn0
route add -net 10.10.10.0/24 gw 10.10.30.1
route add -net 10.10.20.0/24 gw 10.10.30.1# 用来做snat
iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o ens37  -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.10.20.0/24 -o ens37  -j MASQUERADEnode1节点
route add -net 10.10.10.0/24 gw 10.10.30.1
route add -net 10.10.20.0/24 gw 10.10.30.1iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o ens37  -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.10.20.0/24 -o ens37  -j MASQUERADE

在master的node1节点添加了路由规则和snat规则,路由规则的目的是将外界访问ovn内的网络的报文引入到join子网。snat规则的目的是将ovn内部访问外部的报文进行snat转换,如果不进行snat转换不能够收到返回的报文。

添加端口组和ovn策略路由

对应端口组包含了节点上分配的端口,从而实现分布式网关

ovn-nbctl lr-policy-add lr1 31000 ip4.dst==10.10.10.0/24 allow
ovn-nbctl lr-policy-add lr1 31000 ip4.dst==10.10.20.0/24 allow
ovn-nbctl lr-policy-add lr1 31000 ip4.dst==10.10.30.0/24 allow# 将访问master和node1的流量进行reroute
ovn-nbctl lr-policy-add lr1 30000 ip4.dst==192.168.100.100 reroute 10.10.30.2
ovn-nbctl lr-policy-add lr1 30000 ip4.dst==192.168.100.200 reroute 10.10.30.3# 匹配端口组来的流量,将之reroute
ovn-nbctl lr-policy-add lr1 29000 "ip4.src == \$ls1.master_ip4" reroute 10.10.30.2
ovn-nbctl lr-policy-add lr1 29000 "ip4.src == \$ls1.node1_ip4" reroute 10.10.30.3
ovn-nbctl lr-policy-add lr1 29000 "ip4.src == \$ls2.master_ip4" reroute 10.10.30.2
ovn-nbctl lr-policy-add lr1 29000 "ip4.src == \$ls2.node1_ip4" reroute 10.10.30.3# 添加ls1.master端口组,并将ls1-vm2 ls1-vm1加入到端口组中
ovn-nbctl pg-add ls1.master ls1-vm2 ls1-vm1
# 添加ls2.node1端口组,并将ls2-vm2 ls2-vm1添加到端口组中
ovn-nbctl pg-add ls2.node1 ls2-vm2 ls2-vm1

在kube-ovn中根据节点名字和子网名字创建了端口组,比如在我们的拓扑中有两个节点master和node1,并且创建了两个逻辑交换机ls1和ls2,因此会在ovn中创建四个端口组:ls1.master、ls2.master、ls1.node1、ls2.node2。在对应节点和子网上创建的端口会添加到对应的端口组,并且添加了ovn的路由策略通过匹配报文的源ip地址所在的端口组来决定将报文发送到哪个节点的ovn0网卡,通过这种方式实现了分布式网关。

源码分析 1

下面是初始化和构建默认vpc拓扑的流程

Kube-ovn controller在启动时会进行初始化,下面是一些初始化的过程

c.InitOVN()函数

这个函数会创建默认的vpc,并且在默认vpc中创建join子网

  1. 创建vpc的ovn逻辑路由器

  2. 创建默认vpc的join子网,这个子网给k8s集群的每个节点分配一个ip

  3. 创建默认vpc的ovn-default子网,在没有指定subnet的pod使用这个子网分配ip

c.InitDefaultVpc()函数

这个函数更新默认vpc的status信息

c.InitIPAM()函数

这个函数主要用来初始化kube-ovn controller的ipam模块,将所有已经定义的subnet和已经使用了的ip地址记录到ipam中,为后面创建pod分配ip地址做准备

  1. 遍历了subnet资源,将subnet信息存入ipam中

  2. 遍历了ippool资源,将ippools信息存入ipam中

  3. 遍历了ips对象,将已经使用的ip记录到ipam中,当给pod分配ip后会创建ips对象

  4. 遍历了pod对象,读取pod中已经分配的ip记录到ipam中

  5. 遍历了vip对象,将ip记录到ipam中

  6. 遍历了eip对象,将ip记录到ipam中

  7. 遍历了node对象,将ip记录到ipam中

initNodeRoutes()函数

初始化ovn的路由策略,对于pod访问master和node1节点的流量添加了ovn的路由策略。

在我的k8s集群中这个函数添加了这两条策略,192.168.40.199、192.168.40.201是master和node节点的ip地址,100.64.0.3、100.64.0.2是master和node节点上ovn0网卡的ip地址

//30000 ip4.dst == 192.168.40.199 reroute 100.64.0.3

//30000 ip4.dst == 192.168.40.201 reroute 100.64.0.2

  1. 获取了所有的节点,并且获取了节点的ip和节点ovn0的ip

  2. 调用migrateNodeRoute添加ovn策略路由

c.addNodeGwStaticRoute()函数

这个函数主要是给默认vpc的逻辑路由器添加静态路由。

  1. 添加默认vpc的逻辑路由器静态路由 0.0.0.0/0 100.64.0.1 dst-ip

源码分析 2

Kube-ovn controller在启动时根据watch-list机制会调用node添加,下面是节点添加的流程

  1. 给新节点从join子网中分配ip、mac地址

  2. 在join逻辑交换机上添加端口,表示新节点上的ovn0

  3. 遍历所有子网,以 子网名字.节点名_ip4创建端口组

  4. 添加路由策略 eg:29000 ip4.src == $ovn.default.master_ip4 reroute 100.64.0.2

  5. 上面两步实现了默认vpc的分布式网关功能,后面还需要进行snat

 
func (c *Controller) handleAddNode(key string) error {c.nodeKeyMutex.LockKey(key)defer func() { _ = c.nodeKeyMutex.UnlockKey(key) }()cachedNode, err := c.nodesLister.Get(key)if err != nil {if k8serrors.IsNotFound(err) {return nil}return err}node := cachedNode.DeepCopy()klog.Infof("handle add node %s", node.Name)subnets, err := c.subnetsLister.List(labels.Everything())if err != nil {klog.Errorf("failed to list subnets: %v", err)return err}//TODO 获取internalIp,是k8s通信的ip地址nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)//TODO 这个循环的作用是判断node上的ip地址是否和默认vpc下subnet的cidr冲突,如果冲突会发送警告事件for _, subnet := range subnets {//TODO 只和默认vpc相关if subnet.Spec.Vpc != c.config.ClusterRouter {continue}v4, v6 := util.SplitStringIP(subnet.Spec.CIDRBlock)if subnet.Spec.Vlan == "" && (util.CIDRContainIP(v4, nodeIPv4) || util.CIDRContainIP(v6, nodeIPv6)) {msg := fmt.Sprintf("internal IP address of node %s is in CIDR of subnet %s, this may result in network issues", node.Name, subnet.Name)klog.Warning(msg)c.recorder.Eventf(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: node.Name, UID: types.UID(node.Name)}}, v1.EventTypeWarning, "NodeAddressConflictWithSubnet", msg)break}}//TODO 没有太理解providerif err = c.handleNodeAnnotationsForProviderNetworks(node); err != nil {klog.Errorf("failed to handle annotations of node %s for provider networks: %v", node.Name, err)return err}//TODO 获取join子网subnet, err := c.subnetsLister.Get(c.config.NodeSwitch)if err != nil {klog.Errorf("failed to get node subnet: %v", err)return err}//TODO 下面这一坨代码需要获取node的ovn0的ip、mac地址,如果不存在则需要随机分配一个地址var v4IP, v6IP, mac stringportName := fmt.Sprintf("node-%s", key)if node.Annotations[util.AllocatedAnnotation] == "true" && node.Annotations[util.IPAddressAnnotation] != "" && node.Annotations[util.MacAddressAnnotation] != "" {macStr := node.Annotations[util.MacAddressAnnotation]v4IP, v6IP, mac, err = c.ipam.GetStaticAddress(portName, portName, node.Annotations[util.IPAddressAnnotation],&macStr, node.Annotations[util.LogicalSwitchAnnotation], true)if err != nil {klog.Errorf("failed to alloc static ip addrs for node %v: %v", node.Name, err)return err}} else {v4IP, v6IP, mac, err = c.ipam.GetRandomAddress(portName, portName, nil, c.config.NodeSwitch, "", nil, true)if err != nil {klog.Errorf("failed to alloc random ip addrs for node %v: %v", node.Name, err)return err}}ipStr := util.GetStringIP(v4IP, v6IP)//TODO 在join逻辑交换机上给node创建ovs端口if err := c.OVNNbClient.CreateBareLogicalSwitchPort(c.config.NodeSwitch, portName, ipStr, mac); err != nil {return err}//TODO 添加策略路由,这个ip是节点join子网的ipfor _, ip := range strings.Split(ipStr, ",") {if ip == "" {continue}nodeIP, af := nodeIPv4, 4if util.CheckProtocol(ip) == kubeovnv1.ProtocolIPv6 {nodeIP, af = nodeIPv6, 6}//TODO 为了添加这个路由策略30000 ip4.dst == 192.168.40.199  reroute 100.64.0.3if nodeIP != "" {//TODO 这个便是集群node节点通信的地址策略,做reroutevar (match       = fmt.Sprintf("ip%d.dst == %s", af, nodeIP)action      = kubeovnv1.PolicyRouteActionRerouteexternalIDs = map[string]string{"vendor":         util.CniTypeName,"node":           node.Name,"address-family": strconv.Itoa(af),})klog.Infof("add policy route for router: %s, match %s, action %s, nexthop %s, externalID %v", c.config.ClusterRouter, match, action, ip, externalIDs)if err = c.addPolicyRouteToVpc(c.config.ClusterRouter,//TODO 匹配目的ip是节点internal ip,action是reroute,下一条ip是节点上ovn0的ip&kubeovnv1.PolicyRoute{Priority:  util.NodeRouterPolicyPriority,Match:     match,Action:    action,NextHopIP: ip,},externalIDs,); err != nil {klog.Errorf("failed to add logical router policy for node %s: %v", node.Name, err)return err}if err = c.deletePolicyRouteForLocalDNSCacheOnNode(node.Name, af); err != nil {return err}if c.config.NodeLocalDNSIP != "" {if err = c.addPolicyRouteForLocalDNSCacheOnNode(portName, ip, node.Name, af); err != nil {return err}}}}//TODO 添加静态路由,将目的地址是0.0.0.0/0的下一条为join子网网关。if err := c.addNodeGwStaticRoute(); err != nil {klog.Errorf("failed to add static route for node gw: %v", err)return err}patchPayloadTemplate := `[{"op": "%s","path": "/metadata/annotations","value": %s}]`op := "replace"if len(node.Annotations) == 0 {node.Annotations = map[string]string{}op = "add"}//TODO 设置了node在join子网的地址、网关信息node.Annotations[util.IPAddressAnnotation] = ipStrnode.Annotations[util.MacAddressAnnotation] = macnode.Annotations[util.CidrAnnotation] = subnet.Spec.CIDRBlocknode.Annotations[util.GatewayAnnotation] = subnet.Spec.Gatewaynode.Annotations[util.LogicalSwitchAnnotation] = c.config.NodeSwitchnode.Annotations[util.AllocatedAnnotation] = "true"node.Annotations[util.PortNameAnnotation] = fmt.Sprintf("node-%s", key)raw, _ := json.Marshal(node.Annotations)//TODO 构造patch请求,修改node的注解信息patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw)_, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), key, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, "")if err != nil {klog.Errorf("patch node %s failed: %v", key, err)return err}//TODO 给node节点创建的ovn0创建ipsif err := c.createOrUpdateCrdIPs("", ipStr, mac, c.config.NodeSwitch, "", node.Name, "", ""); err != nil {klog.Errorf("failed to create or update IPs node-%s: %v", key, err)return err}//TODO 不太理解这个//TODO 给所有默认vpc下子网都添加这个路由策略$ovn.default.master_ip4这个东西不太理解,个人的理解是匹配指定子网在指定节点,我自己配的没起作用。再从代码看$ovn.default.master_ip4是端口组的名字,//TODO 每次创建pod的时候会根据pod调度的节点来将pod的port id添加到对应子网节点的端口组里,这里又添加了策略路由匹配端口组来选择reroute到哪个ovn0,从而实现了分布式网关//TODO 会添加这个策略 29000 ip4.src == $ovn.default.master_ip4 reroute 100.64.0.2for _, subnet := range subnets {//TODO 只关注默认vpc下的子网if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch || subnet.Spec.GatewayType != kubeovnv1.GWDistributedType {continue}if err = c.createPortGroupForDistributedSubnet(node, subnet); err != nil {klog.Errorf("failed to create port group for node %s and subnet %s: %v", node.Name, subnet.Name, err)return err}if err = c.addPolicyRouteForDistributedSubnet(subnet, node.Name, v4IP, v6IP); err != nil {klog.Errorf("failed to add policy router for node %s and subnet %s: %v", node.Name, subnet.Name, err)return err}// policy route for overlay distributed subnet should be reconciled when node ip changedc.addOrUpdateSubnetQueue.Add(subnet.Name)}// ovn acl doesn't support address_set name with '-', so replace '-' by '.'pgName := strings.ReplaceAll(node.Annotations[util.PortNameAnnotation], "-", ".")if err = c.OVNNbClient.CreatePortGroup(pgName, map[string]string{networkPolicyKey: "node" + "/" + key}); err != nil {klog.Errorf("create port group %s for node %s: %v", pgName, key, err)return err}//TODO 添加策略路由,不理解。看着也是遍历subnet添加路由策略,和上面遍历subnet添加路由策略有什么区别if err := c.addPolicyRouteForCentralizedSubnetOnNode(node.Name, ipStr); err != nil {klog.Errorf("failed to add policy route for node %s, %v", key, err)return err}if err := c.UpdateChassisTag(node); err != nil {return err}return c.retryDelDupChassis(util.ChasRetryTime, util.ChasRetryIntev+2, c.cleanDuplicatedChassis, node)
}

 

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

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

相关文章

Python编程中的异常处理

什么是异常? 程序错误(errors)有时也被称为程序异常(exceptions),这是每个编程人员都会经常遇到的问题。在过去,当遇到这类情况时,程序会终止执行并显示错误信息,通常是…

JAVA之Java线程核心详解

Java线程核心 1.进程和线程 进程:进程的本质是一个正在执行的程序,程序运行时系统会创建一个进程,并且给每个进程分配独立的内存地址空间保证每个进程地址不会相互干扰。同时,在 CPU 对进程做时间片的切换时,保证进程…

微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️系列专栏:Golang全栈-专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站,通俗易懂&…

猫头虎博客分享:深入解析 Visual Studio Code 1.86 版本新特性

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …

测试文章笔记-SQL3种优化方法

SQL语句优化: 本质:降低执行时间 **核心思路:**找到执行计划中开销较高的操作,改写SQL语句或改变表访问方式调整执行计划。 举例: 1.使用索引替代全表扫描(索引:是帮助MysQL高效获取数据的数…

【JVM】打破双亲委派机制

📝个人主页:五敷有你 🔥系列专栏:JVM ⛺️稳中求进,晒太阳 打破双亲委派机制 打破双亲委派机制三种方法 自定义类加载器 ClassLoader包含了四个核心方法 //由类加载器子类实现,获取二进制数据调用…

MySQL之json数据操作

1 MySQL之JSON数据 总所周知,mysql5.7以上提供了一种新的字段格式json,大概是mysql想把非关系型和关系型数据库一口通吃,所以推出了这种非常好用的格式,这样,我们的很多基于mongoDB的业务都可以用mysql去实现了。当然…

NumPy模块完结篇:深入探讨和高效利用【第85篇—NumPy模块】

NumPy模块完结篇:深入探讨和高效利用 NumPy是Python中用于科学计算的核心库之一,提供了高性能的多维数组对象(numpy.ndarray)以及许多用于操作这些数组的函数。在前面的几篇博客中,我们介绍了NumPy的基础知识、数组操…

电子商务跨境电商大数据的关键技术之—主流电商大数据采集

大数据采集是指通过各种技术手段和工具收集、获取和提取大规模数据的过程。在信息时代,各种互联网、物联网、移动设备等的普及和应用,产生了海量的数据,这些数据被称为大数据。大数据采集就是对这些数据进行收集和抓取,以获得有意…

手把手一起开发SV4E-I3C设备(二)

JEDEC DDR5 SPD Hub Devices例程 DDR5生态系统的核心是SidebandBus Protocol 参考下图,可以将SV4E-I3C的端口1声明为主服务器(模拟主机控制器),并且它可以属于SV4E-I3C上的一个总线。端口2可以作为SPD Hub DUT的Local Bus侧的从站连接。这个从站可以被…

12(S)-HETE ELISA kit--灵敏的ELISA试剂盒

灵敏的ELISA试剂盒,能够检测任何物种的培养上清液和血浆中的12(S)-HETE HETE是由脂氧合酶代谢花生四烯酸产生的副产物。12(S)-HETE是12(S)-氢过氧四烯酸(12(S)-HpETE)还原的立体特异性羟基产物,其本身是花生四烯酸的12-脂氧酶代谢…

23年秋招结束,同学们陆陆续续拿到心仪的offer!24年秋招出发!

续接上次上岸同学的分享:还在担心秋招吗?看看24届已上岸同学的经验分享! 秋招的时间过程相同,但经历却各不相同。学历、专业似乎都影响着同学们的面试经历和感受。校招的面试毫无疑问学历的加持是巨大的优势,学历好会…

票房25亿!《热辣滚烫》的创造性模仿,普通人赚钱的落地方法

最近很火的电影《热辣滚烫》包含了我们很多普通人做点小事儿,赚点小钱非常落地的方法,叫做创造性模仿。 很多人说《热辣滚烫》是翻拍的日本《百元之恋》,知道这个有什么用?就证明贾玲不是那么优秀吗?对我们普通人想赚…

全新超大屏三防加固平板为什么做到Intel core i7的高性能

在小编看来,一款加固三防平板电脑结构规划的思路:一定是要在三防规划中留意取舍的。如果是三防要求高的的商品,则需要将三防规划作为一个主线来做,其他方面环绕三防要求打开,并要有清晰的三防思路和总体思想&#xff0…

java面试微服务篇

目录 目录 SpringCloud Spring Cloud 的5大组件 服务注册 Eureka Nacos Eureka和Nacos的对比 负载均衡 负载均衡流程 Ribbon负载均衡策略 自定义负载均衡策略 熔断、降级 服务雪崩 服务降级 服务熔断 服务监控 为什么需要监控 服务监控的组件 skywalking 业务…

【c++】const引用

Hello everybody!今天给大家讲讲有关const引用部分的知识,因为这部分知识涉及到const与引用直接如何灵活的运用,且不太好理解。所以我认为讲一下这里的知识还是很有必要的! 1.权限可缩小 首先,当我们定义了a,在给a取别…

人工智能学习与实训笔记(二):神经网络之图像分类问题

人工智能专栏文章汇总:人工智能学习专栏文章汇总-CSDN博客 目录 二、图像分类问题 2.1 尝试使用全连接神经网络 2.2 引入卷积神经网络 2.3 分类函数Softmax 2.4 交叉熵损失函数 2.5 学习率优化算法 2.6 图像预处理算法 2.6.1 随机改变亮暗、对比度和颜色等 …

这才是大学生该做的副业,别再痴迷于游戏了!

感谢大家一直以来的支持和关注,尤其是在我的上一个公众号被关闭后,仍然选择跟随我的老粉丝们,你们的支持是我继续前行的动力。为了回馈大家长期以来的陪伴,我决定分享一些实用的干货,这些都是我亲身实践并且取得成功的…

Typora的下载安装(文末有安装包,2024亲测可用)

一、安装步骤 1、首先下载安装包,解压到你的目录下面 2、进入到解压后的文件夹下面,看到如下的内容: 3、双击exe文件开始安装,选择安装目录,并记下安装地址!!! 选择创建桌面快捷方…

2024年腾讯云4核8G12M轻量应用服务器测评,2月更新

4核8G服务器支持多少人同时在线访问?阿腾云的4核8G服务器可以支持20个访客同时访问,关于4核8G服务器承载量并发数qps计算测评,云服务器上运行程序效率不同支持人数在线人数不同,公网带宽也是影响4核8G服务器并发数的一大因素&…