Neutron二层网络服务实现原理
网络
网络(network)是一个隔离的二层网段,类似于物理网络世界中的虚拟 LAN (VLAN)。更具体来讲,它是为创建它的租户而保留的一个广播域,或者被显式配置为共享网段。端口和子网始终被分配给某个特定的网络。根据网络的类型,Neutron network 可以分为:
VLAN network(虚拟局域网) :基于物理 VLAN 网络实现的虚拟网络。共享同一个物理网络的多个 VLAN 网络是相互隔离的,甚至可以使用重叠的 IP 地址空间。每个支持 VLAN network 的物理网络可以被视为一个分离的 VLAN trunk,它使用一组独占的 VLAN ID。有效的 VLAN ID 范围是 1 到 4094。
Flat network:基于不使用 VLAN 的物理网络实现的虚拟网络。每个物理网络最多只能实现一个虚拟网络。
local network(本地网络):一个只允许在本服务器内通信的虚拟网络,不知道跨服务器的通信。主要用于单节点上测试。
GRE network (通用路由封装网络):一个使用 GRE 封装网络包的虚拟网络。GRE 封装的数据包基于 IP 路由表来进行路由,因此 GRE network 不和具体的物理网络绑定。
VXLAN network(虚拟可扩展网络):基于 VXLAN 实现的虚拟网络。同 GRE network 一样, VXLAN network 中 IP 包的路由也基于 IP 路由表,也不和具体的物理网络绑定。
关系:
(1)tenant —- 1:n —– network ——- 1:n ——- subnet (一个 tenant 可以拥有多个 network,一个 network 可以包含多个 subnet)
(2)network ——- 1: n ——- port —— 1:1 — subnet(一个network 可以有多个 port, 每个
port 连接一个 subnet)(若创建虚机时指定的是 net-id,那么虚机将随机地从该 network 包含的 subnet 中分配
IP)
(3)VM —– 1 : n —- NIC —– 1:1 — port(一个 VM 可以有多个 NIC,每个 NIC 连接一个 port)(可以在创建虚机时指定一个或者多个 port)
(4)Tenant —– 1 : n —- Router —– 1 : n —— subnet/ext-network (一个
tenant 可以拥有多个 router,每个 router 在 Neutron network 节点上使用一个 Linux network
namespace,其 ID 就是 neutron router-list 得到的 router 的 ID; 一个 router
连接一个通向外网的 gateway 和多个该 tenant 的 subnet)
(5)network —- 1 : 1 —- Dnamasq —– 1: n —– subnet (一个 network 有一个 Dnsmasq 进程,该进程为多个启动了 DHCP 的 subnet 服务,分配它们拥有的 IP 给虚机)
Neutron 管理的实体如下:
网络: 隔离的 L2 域,可以是虚拟、逻辑或交换,同一个网络中的主机彼此 L2 可见。
子网: IP 地址块,其中每个虚拟机有一个 IP,同一个子网的主机彼此 L3 可见。
端口: 网络上虚拟、逻辑或交换端口。
Linux相关技术
Neutron 的设计目标是实现“网络即服务”,为了达到这一目标,在设计上遵循了基于“软件定义网络”实现网络虚拟化的原则,在实现上充分利用了 Linux 系统上的各种网络相关的技术。
理解了 Linux 系统上的这些概念将有利于快速理解 Neutron 的原理和实现。
涉及的 Linux 网络技术
bridge:网桥,Linux中用于表示一个能连接不同网络设备的虚拟设备,linux中传统实现的网桥类似一个hub设备,而ovs管理的网桥一般类似交换机。
br-int:bridge-integration,综合网桥,常用于表示实现主要内部网络功能的网桥。
br-ex:bridge-external,外部网桥,通常表示负责跟外部网络通信的网桥。
GRE:General Routing Encapsulation,一种通过封装来实现隧道的方式。在openstack中一般是基于L3的gre,即original pkt/GRE/IP/Ethernet
VETH:虚拟ethernet接口,通常以pair的方式出现,一端发出的网包,会被另一端接收,可以形成两个网桥之间的通道。
qvb:neutron veth, Linux Bridge-side
qvo:neutron veth, OVS-side
TAP设备:模拟一个二层的网络设备,可以接受和发送二层网包。
TUN设备:模拟一个三层的网络设备,可以接受和发送三层网包。
iptables:Linux 上常见的实现安全策略的防火墙软件。
Vlan:虚拟 Lan,同一个物理 Lan 下用标签实现隔离,可用标号为1-4094。
VXLAN:一套利用 UDP 协议作为底层传输协议的 Overlay 实现。一般认为作为 VLan 技术的延伸或替代者。
namespace:用来实现隔离的一套机制,不同 namespace 中的资源之间彼此不可见。
GRE网络, Vlan网络, Vxlan网络
举例GRE网络
在 VM1 中,虚拟机的网卡实际上连接到了物理机的一个 TAP 设备(即 A,常见名称如 tap-XXX)上,A 则进一步通过 VETH pair(A-B)连接到网桥 qbr-XXX 的端口 vnet0(端口 B)上,之后再通过 VETH pair(C-D)连到 br-int 网桥上。一般 C 的名字格式为 qvb-XXX,而 D的名字格式为 qvo-XXX。注意它们的名称除了前缀外,后面的 id 都是一样的,表示位于同一个虚拟机网络到物理机网络的连接上。
br-tun转发逻辑:
表 10 负责学习。有一条规则,基于 learn 行动来创建反向(内部网包从 gre 端口发出去)的规则。如下所示:
learn 行动并非标准的 openflow 行动,是 openvswitch 自身的扩展行动,这个行动可以根据流内容动态来修改流表内容。这条规则首先创建了一条新的流(该流对应 vm 从 br-tun 的 gre 端口发出的规则):
其中 table=20 表示规则添加在表 20;
NXM_OF_VLAN_TCI[0..11] 表示匹配包自带的vlan id;
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] 表示 L2 目标地址需要匹配当前包的 L2 源地址;
load:0->NXM_OF_VLAN_TCI[],去掉vlan;
load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],添加 tunnel 号为原始 tunnel 号;
output:NXM_OF_IN_PORT[],发出端口为原始包抵达的端口。
向表 20 添加完规则后,最后将匹配的当前网包从端口 1(即 patch-int)发出。
举例VLAN网络:
路由是L3
agent来实现,每个子网在br-int上有一个端口(qr-YYY和qr-ZZZ,已配置IP,分别是各自内部子网的网关),L3
agent绑定到上面。要访问外部的公共网络,需要通过L3
agent发出,而不是经过int-br-ex到phy-br-ex(实际上并没有网包从这个veth
pair传输)。如果要使用外部可见的floating IP,L3 agent仍然需要通过iptables来进行NAT。
在多租户情况下
举例VXLAN网络:(详细分析)
当我们部署环境之后(一个网络节点和两个计算节点)我们查看计算机节点的状态如下,此时并没有vxlan隧道的建立。我们同时发现当创建完成网络和子网的时候(创建了7.7.7.0/24子网),依然没有vxlan隧道。
root@com2:~# ovs-vsctl show
f4a15348-e3a9-4834-82ee-fa0beb4e9420
Bridge br-tun
fail_mode: secure
Port br-tun
Interface br-tun
type: internal
Port patch-int
Interface patch-int
type: patch
options: {peer=patch-tun}
Bridge br-int
fail_mode: secure
Port patch-tun
Interface patch-tun
type: patch
options: {peer=patch-int}
Port br-int
Interface br-int
type: internal
只有当有虚拟机接入该子网时候,我们发现虚拟机所在的计算节点会和网络节点建立vxlan隧道。
root@com2:~# ovs-vsctl show
f4a15348-e3a9-4834-82ee-fa0beb4e9420
Bridge br-tun
fail_mode: secure
Port br-tun
Interface br-tun
type: internal
Port patch-int
Interface patch-int
type: patch
options: {peer=patch-tun}
Port "vxlan-0a0026df"
Interface "vxlan-0a0026df"
type: vxlan
options: {df_default="true", in_key=flow, local_ip="10.0.38.218", out_key=flow, remote_ip="10.0.38.223"}
Bridge br-int
fail_mode: secure
Port patch-tun
Interface patch-tun
type: patch
options: {peer=patch-int}
Port "qvo0fc2bc51-8d"
tag: 3
Interface "qvo0fc2bc51-8d"
Port br-int
Interface br-int
type: internal
Bridge br-trans
Port br-trans
Interface br-trans
type: internal
Port "eth2"
Interface "eth2"
Port "tra0fc2bc51-8d"
Interface "tra0fc2bc51-8d"
ovs_version: "2.5.0"
我们查看br-tun的流表如下(开启了l2 population) :
当数据包由in_port = 1(也就是由br-int发出)到达table 0时候,会转给table 2;如果是arp广播请求,因为开启l2 population,也就是arp_responder之后,会提交给 table 21。此时table21有条规则会处理这个请求,这条规则专门由l2 population发来的 entry 来更新 。
table 21 的更新过程并非网上所讲的那么简单,实际过程如下:假如我们创建了网络A和网络B ,有2个计算机点和1个网络节点。创建一台虚拟机aa加入网络A,同时落地在计算节点1时候,本计算节点的table21只有dhcp的arp的回应记录。再创建一虚拟机bb加入网络B,同时落地在计算节点2时候,本计算节点的table21只有dhcp的arp的回应记录,计算节点1的table21并没有bb的arp的回应记录。创建第三台虚拟机加入网络A,如果落地在计算节点1时候,两个计算节点的table21没有任何更新。当创建第四台虚拟机加入网络A,同时落地在计算机点2时候,计算机点2的table 21更新了所有在计算节点1的同一网络的虚拟机的arp信息,同时table 20记录了所有到达计算机点1同一网络的虚拟机的规则。计算节点1的table21也更新了计算节点2的同网络的虚拟机的arp回应,table20也记录了到达其的规则。
br.add_flow(table=21, priority=1, proto=’arp’, dl_vlan=local_vid, nw_dst= ip, actions=actions) 其中action为:
actions = (‘move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],’ – Place the source MAC address of the request (The requesting VM) as the new reply’s destination MAC address
‘mod_dl_src:%(mac)s,’ – Put the requested MAC address of the remote VM as this message’s source MAC address
‘load:0x2->NXM_OF_ARP_OP[],’ – Put an 0x2 code as the type of the ARP message. 0x2 is an ARP response.
‘move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],’ – Place the ARP request’s source hardware address (MAC) as this new message’s ARP target / destination hardware address
‘move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],’ – Place the ARP request’s source protocol / IP address as the new message’s ARP destination IP address
‘load:%(mac)#x->NXM_NX_ARP_SHA[],’ – Place the requested VM’s MAC address as the source MAC address of the ARP reply
‘load:%(ip)#x->NXM_OF_ARP_SPA[],’ – Place the requested VM’s IP address as the source IP address of the ARP reply
‘in_port’ % {‘mac’: mac, ‘ip’: ip}) – Forward the message back to the port it came in on
Here’s the match part:
self.tun_br.add_flow(table=constants.ARP_RESPONDER, – Add this new flow to the ARP_RESPONDER table
priority=1, – With a priority of 1 (Another, default flow with the lower priority of 0 is added elsewhere in the code)
proto=‘arp’, – Match only on ARP messages
dl_vlan=lvid, – Match only if the destination VLAN (The message has been locally VLAN tagged by now) matches the VLAN ID / network of the remote VM
nw_dst=‘%s‘ % ip, – Match on the IP address of the remote VM in question
actions=actions)
如果table21还是无法解决arp的问题就发给table22 去 flood 到所有端口。如果不是arp请求就转到table 20 ,通过已知学习到的mac地址从相应的口发出,如果不能处理也是发给table 22去flood到所有的端口。
当数据包从in_port=4(也就是由发往br-int)到达table 0时候,会提交给table 4, 它根据tunel号修改为内部vlan号,然后提交给table 10 ,它进行学习更新table 20,然后从patch-int发出。
table 10使用了 openvswitch 的 learn 动作。该动作能根据处理的流来动态修改其它表中的规则。因为此时访问虚拟机的只有dhcp服务,我们看到table 20有一条到达dhcp的流表,这个流表就是这个learn动作生成的。
table=20 说明是修改表 20 中的规则,后面是添加的规则内容;
NXM_OF_VLAN_TCI[0..11],匹配跟当前流同样的 VLAN 头,其中 NXM 是 Nicira Extensible Match 的缩写 ,dl_vlan=3;
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],将 mac source address 记录,所以结果中有 dl_dst= fa:16:3e:c0:0e:07;
load:0->NXM_OF_VLAN_TCI[],在发送出去的时候,vlan tag设为0,所以结果中有 actions=strip_vlan;
load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],发出去的时候,设置 tunnul id,所以结果中有set_tunnel: 0x8;
output:NXM_OF_IN_PORT[],指定发送给哪个port,由于是从 port 4进来的,因而结果中有output4。
root@com2:~# ovs-ofctl show br-tun
OFPT_FEATURES_REPLY (xid=0x2): dpid:00005e38cc7c8e43
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(patch-int): addr:ea:ff:57:bb:5f:2a
config: 0
state: 0
speed: 0 Mbps now, 0 Mbps max
4(vxlan-0a0026df): addr:02:1c:31:dd:58:08
config: 0
state: 0
speed: 0 Mbps now, 0 Mbps max
LOCAL(br-tun): addr:5e:38:cc:7c:8e:43
config: PORT_DOWN
state: LINK_DOWN
speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
root@com2:~# ovs-ofctl dump-flows br-tun
NXST_FLOW reply (xid=0x4):
cookie=0xae4f2288fae3c173, duration=75532.472s, table=0, n_packets=14490, n_bytes=1290354, idle_age=0, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
cookie=0xae4f2288fae3c173, duration=84.773s, table=0, n_packets=2, n_bytes=732, idle_age=73, priority=1,in_port=4 actions=resubmit(,4)
cookie=0xae4f2288fae3c173, duration=75532.472s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=846, n_bytes=50760, idle_age=0, hard_age=65534, priority=1,arp,dl_dst=ff:ff:ff:ff:ff:ff actions=resubmit(,21)
cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=13547, n_bytes=1230482, idle_age=709, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=97, n_bytes=9112, idle_age=31, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)
cookie=0xae4f2288fae3c173, duration=75532.471s, table=3, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
cookie=0xae4f2288fae3c173, duration=85.471s, table=4, n_packets=2, n_bytes=732, idle_age=73, priority=1,tun_id=0x8 actions=mod_vlan_vid:3,resubmit(,10)
cookie=0xae4f2288fae3c173, duration=75532.470s, table=4, n_packets=2, n_bytes=140, idle_age=2866, hard_age=65534, priority=0 actions=drop
cookie=0xae4f2288fae3c173, duration=75532.470s, table=6, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
cookie=0xae4f2288fae3c173, duration=75532.470s, table=10, n_packets=12541, n_bytes=1237278, idle_age=73, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xae4f2288fae3c173,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1
cookie=0xae4f2288fae3c173, duration=84.772s, table=20, n_packets=0, n_bytes=0, idle_age=84, priority=2,dl_vlan=3,dl_dst=fa:16:3e:c0:0e:07 actions=strip_vlan,set_tunnel:0x8,output:4
cookie=0xae4f2288fae3c173, duration=73.586s, table=20, n_packets=0, n_bytes=0, hard_timeout=300, idle_age=73, priority=1,vlan_tci=0x0003/0x0fff,dl_dst=fa:16:3e:c0:0e:07 actions=load:0->NXM_OF_VLAN_TCI[],load:0x8->NXM_NX_TUN_ID[],output:4
cookie=0xae4f2288fae3c173, duration=75532.470s, table=20, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=resubmit(,22)
cookie=0xae4f2288fae3c173, duration=84.772s, table=21, n_packets=0, n_bytes=0, idle_age=84, priority=1,arp,dl_vlan=3,arp_tpa=7.7.7.2 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:c0:0e:07,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163ec00e07->NXM_NX_ARP_SHA[],load:0x7070702->NXM_OF_ARP_SPA[],IN_PORT
cookie=0xae4f2288fae3c173, duration=75532.469s, table=21, n_packets=827, n_bytes=49620, idle_age=0, hard_age=65534, priority=0 actions=resubmit(,22)
cookie=0xae4f2288fae3c173, duration=84.758s, table=22, n_packets=84, n_bytes=5614, idle_age=0, dl_vlan=3 actions=strip_vlan,set_tunnel:0x8,output:4
cookie=0xae4f2288fae3c173, duration=75532.462s, table=22, n_packets=31, n_bytes=2778, idle_age=84, hard_age=65534, priority=0 actions=drop
下图是没有开启l2popluation功能,从上述流表看出与开启l2popluation功能不同。
L2 Population
L2 Population 是用来提高 VXLAN 网络 Scalability 的,减少广播风暴。
这是一个包含 5 个节点的 VXLAN 网络,每个节点上运行了若干 VM。现在假设 Host 1 上的 VM A 想与 Host 4 上的 VM G 通信,VM A 要做的第一步是获知 VM G 的 MAC 地址。 于是 VM A 需要在整个 VXLAN 网络中广播 APR 报文:“VM G 的 MAC 地址是多少?” 如果没有L2poluation,情况会如下:
L2 Population 的作用是在 VTEP 上提供 Porxy ARP 功能,使得 VTEP 能够预先获知 VXLAN 网络中,包括VM IP – MAC 对应关系和 VM – VTEP 的对应关系。当 Host 1 上的 同网段的VM A 想与 Host 4 上的同网段的 VM G 通信时,Host 1 上的 VTEP 直接响应 VM A 的 APR 请求即可(为什么HOST1 上会响应,参看上文),告诉VM G的MAC地址。当Host 1 上的VMA想与不同网段的VM G 通信时,首先Host 1 上的 VTEP 响应 VMA所在网络的网关的arp信息(当路由器创建并关联网络时候,L2 Population会使得该网络所跨的所有HOST的VTEP更新该路由器的arp回应信息和如何到达的该路由器的流表,说具体就是table21和table20的更新),数据包会发送给路由器,到达路由器之后,如果路由器同时关联着VM G 所在的网络,它就会发送arp广播请求VM G 的mac,此时网络节点的table21会响应(L2 Population的功能)VM G的信息,路由器知道了VM G的mac地址后,发送单播,由table0到table2,再由table20匹配相关流表,tunnel发出。数据包到达VM G所在的HOST之后,首先匹配table0,然后转给table4,table4去tunnel转给table10, table10 学习完存到table 20之后从patch-int口仍给br-int。br-int相当于一个普通的交换机,将数据包转发给VM G。
另外我还发现如果开启了l2population之后,table10的学习可以去掉。
ovs-ofctl add-flow br-tun "table=10, n_packets=0, n_bytes=0, idle_age=62653, priority=1 actions=output:1"
Neutron二层网络服务实现原理的更多相关文章
- Neutron三层网络服务实现原理
Neutron 对虚拟三层网络的实现是通过其 L3 Agent (neutron-l3-agent).该 Agent 利用 Linux IP 栈.route 和 iptables 来实现内网内不同网络 ...
- neutron二层网络实现
一.基本概念 1.tap设备 比如vnet0,是虚拟化技术和KVM和XEN用来实现虚拟网卡的,当一个以太网帧发送给TAP设备时,这个以太网帧就会被虚拟机操作系统所接手,命名空间用于隔离虚拟网络设备. ...
- OpenStack之Neutron网络服务(一)
1.Neutron概要 OpenStack网络服务提供了一个API接口,允许用户在云上设置和定义网络连接和地址.这个网络服务的项目代码名称是Neutron.OpenStack网络处理虚拟设备的创建和管 ...
- openstack——neutron网络服务
一.neutron 介绍: Neutron 概述 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是在多租户场景里,用户随时都可能需要 ...
- OpenStack核心组件-neutron网络服务
1. neutron 介绍 1.1 Neutron 概述 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是在多租户场景里,用户随时都可能 ...
- openstack核心组件--neutron网络服务2(4)
一.虚拟机获取 ip: 用 namspace 隔离 DHCP 服务 Neutron 通过 dnsmasq 提供 DHCP 服务,而 dnsmasq 通过 Linux Network Names ...
- OpenStack组件——Neutron网络服务(2)
1.虚拟机获取 ip 1)用 namspace 隔离 DHCP 服务 Neutron 通过 dnsmasq 提供 DHCP 服务,而 dnsmasq 通过 Linux Network Namespac ...
- openstack核心组件——neutron网络服务 抓取ip(9)
云计算openstack核心组件——neutron网络服务(9) 一.虚拟机获取 ip: 用 namspace 隔离 DHCP 服务 Neutron 通过 dnsmasq 提供 DHCP 服务 ...
- 云计算openstack核心组件——neutron网络服务(9)
一.虚拟机获取 ip: 用 namspace 隔离 DHCP 服务 Neutron 通过 dnsmasq 提供 DHCP 服务,而 dnsmasq 通过 Linux Network Namespa ...
随机推荐
- Silly Java-Final 关键字
Final 关键字 adj. 最终的:最后的:决定性的:不可改变的 1.修饰变量 final variable 意味 [最后的变量,不可改变的变量即常量] Java中该关键字即代表常量 修饰基本类型的 ...
- 【转】Google的2012论文
转自:http://www.sigvc.org/bbs/thread-1152-1-1.html Google的论文一直是业界的风向标,尤其在机器学习.分布式系统.网络等方面很多创新性的成果都是由他们 ...
- 性能调优之MySQL篇一:MySQL性能计数器
计数器 计数器分析 Threads_connected 表示当前有多少个客户连接该mysql服务器,连接数是否过多,网络是否存在问题,它是动态变化的,当达到最大连接数时,数据库系统就不能提供更多的连接 ...
- eclipse调整字体大小
window->preferences->general->appearance->colors and fonts-> 双击Text Font 就调整字体大小了
- https://www.cnblogs.com/skywang12345/category/455711.html
https://www.cnblogs.com/skywang12345/category/455711.html
- MySql中concat函数的用法(链接字符串)
MySQL中concat函数使用方法:CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意:如果所有参数均为非二进制字符串 ...
- JavaScript 事件处理详解
事件绑定与解绑: el.onEventName = function (){} document.getElementById("dom").onclick = function( ...
- Http:UTF-8与GB2312之间的关系
UTF-8里包括GB2312.UTF-8是国际通用的标准(包括世界所有的语言),而GB2312(只是简体中文)只适合做中文的网站.假设你想做个中文网页,但是还可以翻成英文的话,就得用UTF-8.如果用 ...
- pom.xml常用元素解析
project 最外层元素 modelVersion 指定Maven模型的版本号,对于Maven2和Maven3,它只能是4.0.0 version 版本信息 groupId 包id,会生成相应路径 ...
- FactoryBean
总结自:https://www.cnblogs.com/davidwang456/p/3688250.html Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即xxxF ...