当云原生网关遇上图数据库,NebulaGraph的APISI
本文介绍了利用开源API网关APISIX加速NebulaGraph多个场景的落地最佳实践:负载均衡、暴露接口结构与TLSTermination。API网关介绍什么是API网关
API网关是位于客户端和服务器之间的中间人,用于管理、监控和保护API。它可以在API之前执行一些操作,例如:身份验证、授权、缓存、日志记录、审计、流量控制、安全、防火墙、压缩、解压缩、加密、解密等。
API网关可以工作在TCPIP4层和OSI7层。跑在7层的API网关可以使用多种协议,例如:HTTP、HTTPS、WebSocket、gRPC、MQTT等。在这些应用层协议中做一些操作,比如,请求的重写、转发、合并、重试、缓存、限流、熔断、降级、鉴权、监控、日志、审计等等。
这里举例一下借助API网关可以做的具体的事:在网关层增加认证层,比如:JWT认证、OAuth2认证、OpenID认证等等,这样不需要在每个服务中都做具体的认证集成工作,进而节省许多开发成本。借助网关给跳板机SSH流量增加无需客户端修改的复杂认证,比如:跳转任何客户端的SSH登录,给出一个网址或者输入框,引导登陆者通过网页的SSO认证(包含多因素认证),再通过网关转发到SSH服务。甚至在网关层做Serverless数据库!TiDB社区的同学们就在做这个事儿,他们从普通的MySQL客户端的登录请求中解析能推断出转到需要的TiDB示例的信息,并且在需要coldstart唤醒实例的时候把连接保持住,可以参考这篇文章:TiDBGateway。如果你特别惨在维护屎山项目,不得不针对旧版本的应用程序对新版本的服务端进行兼容,这时候API网关也可以通过一些请求重写,把旧版本的请求转换成新版本的请求。
只要脑洞大,理论上API网关可以做很多事。但显然不是所有的事情都是适合在这一层去做的,通常那些比较通用的事情才适合在这一层去做,上面我只是给出一些典型和极端的具体例子。ApacheAPISIX
API网关是从LB、ReverseProxy项目演进过来的。随着云原生的兴起,API网关也逐渐成为了云原生的一部分,流行的开源网关有:NginxApacheAPISIXKongLuraOpenRestyTykTraefikIstioEnvoy
而且其中很多都是基于NginxOpenResty的下游项目。这里就以ApacheAPISIX为例,介绍一下NebulaGraph借助API网关的几个实践。NebulaGraph介绍
NebulaGraph是一个开源的分布式图数据库,它的特点是:高性能:可达到每秒百万级的读写,具有极高的扩展性,在千亿点、万亿边的数据规模下支持毫秒级的查询。易扩展:分布式的架构可在多台机器上扩展。每台机器上可以运行多个服务进程,它的查询层是无状态的计算存储分离架构,可以容易地引入不同配置、不同类型的计算层,实现同一集群上TP、AP、图计算等不同负载的混合查询。易使用:类SQL的原生查询语言,易于学习和使用,同时支持openCypher。丰富生态:NebulaGraph的生态系统正在不断壮大,目前已经有了多个客户端,包括Java、Python、Go、C、JavaScript、Spark、Flink等,同时也有了多个可视化工具,包括NebulaGraphStudio、NebulaGraphDashboard、NebulaGraphExplorer等。本文讨论的问题
本文给出了基于NebulaGraph集群应用中涉及到API网关的几个场景。查询接口的负载均衡底层存储接口的暴露传输层的加密查询接口负载均衡
首先是图数据库查询接口graphd的负载均衡与高可用的问题。
NebulaGraph内核由三种服务组成:graphd、metad和storaged:
所以,在默认情况下,集群只会暴露graphd的接口,提供给客户端连接,执行nGQL的查询。其中,graphd是无状态的,这意味着可以在多个graphd之间做负载均衡。这里,我们有两种方法:基于客户端的(ClientSideLB)与基于代理的。客户端的负载均衡
客户端的负载均衡,就是在客户端,也就是应用程序中,实现负载均衡的逻辑。NebulaGraph的各个语言的客户端里边已经内置了轮询(RoundRobin)负载均衡,我们只需要在客户端配置多个graphd的地址就可以了。比如,我们在创建连接池的时候,指定了两个不同的graphd的地址(对应不同进程实例),下面以Python代码为例:fromnebula3。gclient。netimportConnectionPoolfromnebula3。ConfigimportConfigconfigConfig()config。maxconnectionpoolsize10connectionpoolConnectionPool()connectionpool。init(〔(127。0。0。1,9669),(127。0。0。1,49433)〕,config)
在取得连接的时候,就会从连接池中随机取得一个连接:In〔10〕:connection0connectionpool。getconnection()In〔11〕:connection1connectionpool。getconnection()这两个连接的graphd地址是不同的In〔12〕:connection0。port,connection1。portOut〔12〕:(9669,49433)
这种客户端负载均衡的问题在于配置、实现细节与应用代码耦合在一起,如果需要修改负载均衡的策略,就要修改应用代码,这样就会增加应用的复杂度。代理的负载均衡
基于代理的负载均衡,就是在应用程序之前,增加一个代理层,来实现负载均衡的逻辑。这样,应用程序就不需要关心负载均衡的问题了。在K8s里的话,我们可以使用K8s的Service来实现这个代理层。
这是一个在Minikube中为NebulaGraph集群中graphd创建的Service:catEOFkubectlcreatefapiVersion:v1kind:Servicemetadata:labels:app。kubernetes。iocluster:nebulaapp。kubernetes。iocomponent:graphdapp。kubernetes。iomanagedby:nebulaoperatorapp。kubernetes。ioname:nebulagraphname:nebulagraphdsvcnodeportnamespace:defaultspec:externalTrafficPolicy:Localports:name:thriftport:9669protocol:TCPtargetPort:9669nodePort:30000name:httpport:19669protocol:TCPtargetPort:19669nodePort:30001selector:app。kubernetes。iocluster:nebulaapp。kubernetes。iocomponent:graphdapp。kubernetes。iomanagedby:nebulaoperatorapp。kubernetes。ioname:nebulagraphtype:NodePortEOF
创建后,我们就可以通过它暴露的单独端口来访问NebulaGraph集群中的graphd了:In〔13〕:connectionpoolConnectionPool()。。。:connectionpool。init(〔(192。168。49。2,9669)〕,config)Out〔13〕:TrueIn〔14〕:connection0connectionpool。getconnection()In〔15〕:connection1connectionpool。getconnection()In〔16〕:connection0。ip,connection1。ipOut〔16〕:(192。168。49。2,192。168。49。2)
可以看到,在连接层面上来看,客户端只知道代理的地址,而不知道NebulaGraph集群中的graphd的地址,这样就实现了客户端与NebulaGraph集群中的graphd的解耦。
然而,当我们在Connection之上创建Session的时候,就能看到实际上客户端的不同请求是落在了不同的graphd上的:In〔17〕:sessionconnectionpool。getsession(root,nebula)In〔18〕:session。sessionidOut〔18〕:1668670607568178In〔19〕:session1connectionpool。getsession(root,nebula)In〔20〕:session1。sessionidOut〔20〕:1668670625563307得到每一个session的IDIn〔21〕:session。execute(SHOWSESSIONS)它们分别对应了两个不同的graphd实例Out〔21〕:ResultSet(keys:〔SessionId,UserName,SpaceName,CreateTime,UpdateTime,GraphAddr,Timezone,ClientIp〕,values:〔1668670607568178,root,,utcdatetime:20221117T07:36:47。568178,timezoneoffset:0,utcdatetime:20221117T07:36:47。575303,timezoneoffset:0,nebulagraphd0。nebulagraphdsvc。default。svc。cluster。local:9669,0,172。17。0。1〕,〔1668670625563307,root,,utcdatetime:20221117T07:37:05。563307,timezoneoffset:0,utcdatetime:20221117T07:37:03。638910,timezoneoffset:0,nebulagraphd1。nebulagraphdsvc。default。svc。cluster。local:9669,0,172。17。0。1〕)底层存储接口的暴露
在NebulaGraph中,可以通过StorageClient来访问底层的存储接口,这个接口可以用来做一些分析型、数据全扫描计算的工作。
然而,存储层的分布式服务实例不像graphd那样,它们是有状态的。这其实与K8s或者DockerCompose的部署模型是相违背的。如果访问的应用storaged客户端在集群外部,我们需要在NebulaGraph集群中的每一个存储实例上都部署一个代理Service。这非常不方便,有时候还是一种浪费。
此外,由于NebulaGraph内部服务发现机制和storaged客户端的实现机制决定,每一个storaged服务实体都是由其内部的host:port唯一确定和寻址的,这给我们中间的代理工作也带来了一些麻烦。
总结来看,我们的需求是:能够从集群外部访问NebulaGraph的存储层每一个实例每一个实例的访问地址(host:port)和内部的地址是完全一致的
为了实现这个需求,我之前的做法是为每一个实例单独部署一个graphd代理(消耗一个地址,保证端口不变),再在外部手动搭一个Nginx作为代理,配合DNS把内部的地址解析Nginx上,然后通过域名找到上游(每一个单独的graphd代理)。本文的延伸阅读1、2中给出了相关的实验步骤。
最近,我找到了一个相对优雅的可维护的方式:在NebulaGraph集群同一个命名空间下引入一个APISIX网关;利用APISIX中的NginxTCP代理的封装streamproxy来暴露storaged的接口;为了最终只利用一个集群的出口(Service,我们利用其支持的TLSv1。3中的extendhostname字段:SNI来路由上游),做到用不同域名的TCPoverTLS指向后端的不同storaged;只需要Storage客户端能支持TLSv1。3(发送SNI),并且能解析所有storaged的地址到APISIX的Service上即可;
示例图:K8sClusterNebulaGraphClusterAPISIXAPIGATEWAYstoraged0streamproxy。。addr:9559storaged1DNS(Service)tls:true。,SNIstoraged2storaged3
这样做的好处是:在APISIX中比较优雅地维护代理的配置,并且可以用到APISIX现代化的流量管理能力;不需要为每一个storaged单独创建Service,只需要一个Service、集群地址就可以了;为流量增加了TLSv1。3的加密,提高了安全性。同时,没有给NebulaGraph集群内部的南北流量带来的性能损耗;
在本文的结尾,给出了实验过程,包含了本文提到的所有要点和细节。传输层的加密
我们在前一个问题中提及到了,在APISIX网关中terminateTLSv1。3的连接,借助SNI信息路由storaged的方法。其实,单独将graphd接口的TLS交给网关来做,好处也是非常明显的:证书管理在统一的网关控制面做,更加方便;证书运维无NebulaGraph集群配置侵入(NebulaGraph原生支持TLS加密,但是加密之后带来了集群内部通信的开销,而且配置和集群其他层面配置在一起,证书更新涉及进程重启,不够灵活);
具体的方法在后边实操中也是有体现的。实操:利用APISIX的streamproxy暴露storaged的接口实验环境:Minikube
本实验在本地的Minikube上做。首先,启动一个Minikube。因为APISIX内部的etcd需要用到storageclass,我们带上穷人版的storageclass插件。同时,为了在K8s外部访问storaged的时候用和内部相同的域名和端口,将把nodeport允许的端口扩充到小于9779的范围。addonsdefaultstorageclassextraconfigapiserver。servicenodeportrange165535实验环境:NebulaGraphonK8s
这里,我们使用NebulaOperator来部署NebulaGraph集群,具体的部署方法可以参考NebulaOperator文档:https:docs。nebulagraph。com。cn3。3。0nebulaoperator1。introductiontonebulaoperator。
咱们做实验,就偷个懒,用我写的NebulaOperatorKinD来一键部署:curlsLnebulakind。siwei。ioinstallonK8s。shbash实验环境:APISIXonK8s
首先,是安装。在Helm参数中指定打开streamproxy的开关:helmrepoaddapisixhttps:charts。apiseven。comhelmrepoaddbitnamihttps:charts。bitnami。combitnamihelmrepoupdatehelminstallapisixapisixapisixsetgateway。typeNodePortsetgateway。stream。enabledtruesetingresscontroller。enabledtruedashboard也装上,方便我们绕过adminAPIcall做一些方便的操作。helminstallapisixdashboardapisixapisixdashboard
因为截止到现在,APISIX的HelmChart之中并没有提供streamproxyTCP的监听端口的TLS支持的配置格式,见:https:github。comapacheapisixhelmchartissues348。我们需要手动更改APISIX的ConfigMap,把streamproxy的TLS配置加上:kubectleditConfigMapapisix
我们编辑把streamproxy。tcp改写成这样:streamproxy:TCPUDPproxyonly:falsetcp:TCPproxyportlistaddr:9779tls:trueaddr:9559tls:true
这里我们需要重建APISIXPod,因为APISIX的streamproxy的TLS配置是在启动的时候加载的,所以我们需要重建APISIXPod:kubectldelete(kubectlgetpolapp。kubernetes。ionameapisixoname)开始实验
这个实验的目标是把NebulaGraph的storaged的接口暴露出来,让外部的客户端可以访问到,而暴露的方式如图:K8sClusterNebulaGraphClusterAPISIXAPIGATEWAYstoraged0streamproxy。。addr:9559storaged1DNS(Service)tls:true。,SNIstoraged2storaged3
我们已经有了所有的框架,我们要往里填箭头和圆圈就行。kubectlgetpoNAMEREADYSTATUSRESTARTSAGEapisix6d89854bc55m78811Running1(31hago)2d4hapisixdashboardb544bd766nh79j11Running8(31hago)2d10hapisixetcd011Running2(31hago)2d10hapisixetcd111Running2(31hago)2d10hapisixetcd211Running2(31hago)2d10hnebulagraphd011Running2(31hago)3d4hnebulametad011Running2(31hago)3d4hnebulastoraged011Running2(31hago)3d4hnebulastoraged111Running2(31hago)3d4hnebulastoraged211Running2(31hago)3d4h配置APISIX的streamproxy
参考APISIX文档:https:apisix。apache。orgdocsapisixstreamproxyaccepttlsovertcpconnection。
我们用APISIX的API来配置streamproxy:apisixapikeyedd1c9f034335f136f87ad84b625c8f1apisixpod(kubectlgetpolapp。kubernetes。ionameapisixoname)kubectlexecitapisixpodcurlhttp:127。0。0。1:9180apisixadminstreamroutes1HXAPIKEY:apisixapikeyXPUTd{sni:nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local,upstream:{nodes:{172。17。0。13:9779:1},type:roundrobin}}kubectlexecitapisixpodcurlhttp:127。0。0。1:9180apisixadminstreamroutes2HXAPIKEY:apisixapikeyXPUTd{sni:nebulastoraged1。nebulastoragedheadless。default。svc。cluster。local,upstream:{nodes:{172。17。0。18:9779:1},type:roundrobin}}kubectlexecitapisixpodcurlhttp:127。0。0。1:9180apisixadminstreamroutes3HXAPIKEY:apisixapikeyXPUTd{sni:nebulastoraged2。nebulastoragedheadless。default。svc。cluster。local,upstream:{nodes:{172。17。0。5:9779:1},type:roundrobin}}
这里需要注意,目前,APISIX的streamproxy上游节点不支持域名解析是受限于上游的lua库,详见issue:https:github。comapacheapisixissues8334。理想情况下,这里应该给出每一个storaged的SNI相同的地址作为upstream。nodes。像这样:kubectlexecitapisixpodcurlhttp:127。0。0。1:9180apisixadminstreamroutes1HXAPIKEY:apisixapikeyXPUTd{sni:nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local,upstream:{nodes:{nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local:1},type:roundrobin}}配置APISIX中storaged地址的TLS证书
在生产环境下,我们应该以云原生的方式去管理自签或者公共信任的证书。这里,我们就手动利用MKCert工具来做这件事儿。
安装MKCert:首次运行,需要安装mkcert,并且生成根证书macOS的话brewinstallmkcertubuntu的话aptgetinstallwgetlibnss3tools然后再去https:github。comFiloSottilemkcertreleases下载mkcert
签发证书:mkcert。nebulastoragedheadless。default。svc。cluster。local
利用APISIXDashboard将证书导入到APISIX之中。单独开一个终端,运行:exportPODNAME(kubectlgetpodslapp。kubernetes。ionameapisixdashboard,app。kubernetes。ioinstanceapisixdashboardojsonpath{。items〔0〕。metadata。name})exportCONTAINERPORT(kubectlgetpodPODNAMEojsonpath{。spec。containers〔0〕。ports〔0〕。containerPort})kubectlportforwardPODNAME8080:CONTAINERPORTaddress0。0。0。0
浏览器访问:http:10。1。1。168:8080ssllist,账号密码都是admin。点击Create按钮,将刚刚生成的证书导入到APISIX之中。
增加APISIX的NodePortService
创建一个NodePortService,用于暴露APISIX的9779端口。这样,我们就可以通过外部的IP地址访问到APISIX了。catEOFkubectlapplyfspec:selector:app。kubernetes。ioinstance:apisixapp。kubernetes。ioname:apisixports:protocol:TCPport:9779targetPort:9779name:thriftnodePort:9779type:NodePortEOF
因为前边Minikube中我们配置了端口的范围覆盖到了9779,所以我们可以看到,这个NodePortService的端口在宿主机上也可以从Minikubeip的同一个端口访问到:minikubeserviceapisixsvcminikubeservicelistNAMESPACENAMETARGETPORTURL。。。defaultapisixsvcthrift9779http:192。168。49。2:9779。。。
当然,Minikube假设我们的服务都是HTTP的,给出的URL是HTTP:的。不用理会它,我们心里知道它是TCPoverTLS就好了。配置K8s外部DNS
这里需要配置一个DNS服务,让我们可以通过nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local等三个域名通过Minikube的NodePortService访问到NebulaGraph的storaged服务。
获得Minikube的IP地址:minikubeip192。168。49。2
配置etchosts192。168。49。2nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local192。168。49。2nebulastoraged1。nebulastoragedheadless。default。svc。cluster。local192。168。49。2nebulastoraged2。nebulastoragedheadless。default。svc。cluster。local192。168。49。2nebulametad0。nebulametadheadless。default。svc。cluster。local验证NebulaGraphStorageClient可以从所有的节点中获取到数据
这里,为了方便,我们用到Python的客户端。
由于在写本文的时候,NebulaGraphPython客户端的StorageClient尚未支持TLS,对它支持的PR刚好是我为了本实验写的:https:github。comvesoftincnebulapythonpull239。
所以,这里从个人分支安装这个客户端:gitclonehttps:github。comweygunebulapython。gitcdnebulapythonpython3mpipinstall。python3mpipinstallipython进入ipythonipython
我们在iPython中交互式验证:fromnebula3。mclientimportMetaCache,HostAddrfromnebula3。sclient。GraphStorageClientimportGraphStorageClientfromnebula3。ConfigimportSSLconfigimportsslimportosmetacacheMetaCache(〔(nebulametad0。nebulametadheadless。default。svc。cluster。local,9559)〕,50000)storageaddrs〔HostAddr(hostnebulastoraged0。nebulastoragedheadless。default。svc。cluster。local,port9779),HostAddr(hostnebulastoraged1。nebulastoragedheadless。default。svc。cluster。local,port9779),HostAddr(hostnebulastoraged2。nebulastoragedheadless。default。svc。cluster。local,port9779)〕自签证书配置currentdiros。path。abspath(。)sslconfigSSLconfig()sslconfig。certreqsssl。CERTOPTIONALsslconfig。certreqsssl。CERTOPTIONALsslconfig。cacertsos。path。join(os。path。expanduser(。localsharemkcert),rootCA。pem)sslconfig。keyfileos。path。join(currentdir,nebulastoragedheadless。default。svc。cluster。local1key。pem)sslconfig。certfileos。path。join(currentdir,nebulastoragedheadless。default。svc。cluster。local1。pem)实例化StorageClientgraphstorageclientGraphStorageClient(metacache,storageaddrs,5000,sslconfig)验证可以从所有的节点中获取到数据respgraphstorageclient。scanvertex(spacenamebasketballplayer,tagnameplayer)whileresp。hasnext():resultresp。next()forvertexdatainresult:print(vertexdata)
结果:(player112:player{name:JonathonSimmons,age:29})(player117:player{name:StephenCurry,age:31})(player119:player{name:KevinDurant,age:30})(player134:player{name:BlakeGriffin,age:30})(player141:player{name:RayAllen,age:43})(player144:player{name:ShaquilleONeal,age:47})(player149:player{name:BenSimmons,age:22})(player100:player{name:TimDuncan,age:42})(player101:player{name:TonyParker,age:36})(player110:player{name:CoryJoseph,age:27})(player126:player{name:KyrieIrving,age:26})(player131:player{name:PaulGeorge,age:28})(player133:player{name:YaoMing,age:38})(player140:player{name:GrantHill,age:46})(player105:player{name:DannyGreen,age:31})(player109:player{name:TiagoSplitter,age:34})(player111:player{name:DavidWest,age:38})。。。总结NebulaGraph查询接口的负载均衡可以借助K8sService来做;NebulaGraph底层存储接口的暴露在K8s中可以利用APISIXStreamProxy和SNI来优雅实现;利用API网关对出口传输层的加密是一个很好的选择,相较于用NebulaGraph原生的TLS的方式。一些坑
fbthriftPython并不支持发送extendhostname(SNI):https:github。comvesoftincnebulapythonpull238,写了PR去做支持。这时候APISIX中的报错是failedtofindSNI:2022111510:18:26〔error〕7878:1744270stream〔lua〕init。lua:842:streamsslphase():failedtofetchsslconfig:failedtofindSNI:pleasecheckiftheclientrequestsviaIPorusesanoutdatedprotocol。Ifyouneedtoreportanissue,provideapacketcapturefileoftheTLShandshake。,context:sslcertificatebylua,client:172。17。0。1,server:0。0。0。0:9779
参考延伸阅读的36。
此外,我还发现APISIXstream里边不解析上游node域名,我查了所有DNS都没有问题,去提了issue才知道是已知问题:https:github。comapacheapisixissues8334,只好先手配IP:Port作罢。2022111512:26:59〔error〕4444:9538531stream〔lua〕resolver。lua:47:parsedomain():failedtoparsedomain:nebulastoraged0。nebulastoragedheadless。default。svc。cluster。local,error:failedtoquerytheDNSserver:dnsclienterror:101emptyrecordreceivedwhileprereadingclientdata,client:172。17。0。1,server:0。0。0。0:97792022111512:26:59〔error〕4444:9538531stream〔lua〕upstream。lua:79:parsedomainfornodes():dnsresolverdomain:nebulastoraged0。nebulastoragedheadless。default。svc。cluster。localerror:failedtoquerytheDNSserver:dnsclienterror:101emptyrecordreceivedwhileprereadingclientdata,client:172。17。0。1,server:0。0。0。0:97792022111512:26:59〔error〕4444:9538531stream〔lua〕init。lua:965:streamprereadphase():failedtosetupstream:novalidupstreamnodewhileprereadingclientdata,client:172。17。0。1,server:0。0。0。0:9779延伸阅读https:gist。github。comweygu950e4f4c673badae375e59007d80d372https:gist。github。comweygu699b9a2ef5dff5f0fb5f288d692ddfd5https:docs。python。org3libraryssl。htmlssl。SSLContext。sslsocketclasshttps:github。comapachethriftcommit937228e030569bf25ceb379c9491426709792701https:github。comapachethriftpull894https:github。comapachethriftblobe8353cb46e9f5e71f9b76f55d6bf59530b7f98eflibpysrctransportTSSLSocket。pyL184
谢谢你读完本文()
要来近距离体验一把图数据库吗?现在可以用用NebulaGraphCloud来搭建自己的图数据系统哟,快来节省大量的部署安装时间来搞定业务吧NebulaGraph阿里云计算巢现30天免费使用中
想看源码的小伙伴可以前往GitHub阅读、使用、()star它GitHub:https:github。comvesoftincnebula
刘国梁当选国际乒联副主席小球中的博弈知识北京时间2021年11月25日凌晨,2021年国际乒联代表大会在美国休斯敦召开。WTT世界乒乓球职业大联盟理事会主席中国乒协主席刘国梁成功当选国际乒乓球联合会执行副主席。乒乓球属于
曾经的好闺蜜,如今无交流?这几对明星姐妹花,还会和好吗?娱乐圈中明星们之间有真正的友谊吗?答案肯定是一定的,看张一山和杨紫从童星出道以来就一直是最好的朋友,甚至完全处成了好兄弟,不管做出什么举动都不会受到别人的误解。还有何炅和谢娜也是,
岳云鹏家谁说了算?一碗炸酱面就能看出端倪刘筱亭是德云社年轻一代的相声演员,是德云社当红小生岳云鹏的徒弟,和搭档张九泰创作出了不少的经典作品,深受师傅岳云鹏喜欢,经常带着他们去小剧场演出。德云社有句老话说台上无大小,台下立
章子怡继女16岁了,你们看她长得像谁?昨天11月25日,章子怡在深夜卡点2325分为大女儿小苹果庆祝16岁生日,也算是很有心了。众所周知,汪峰的感情史也是很五花八门了,一共有三段婚姻加N个现女友和四个孩子,小苹果是汪峰
40岁宋慧乔又演女精英,戴CHAUMET拎Fendi,职场风穿搭超养眼范主说职场穿搭模板离婚2年,重出江湖的宋慧乔,(在剧中)又和帅哥谈起了恋爱,这次的对象是比她小11岁的鲜肉张基龙。俩人的新剧现在分手中最近正在热播,光看海报就觉得配一脸,拉丝的眼神
镍A股市场17家企业镍的有不易锈蚀磁性大,可以用来制造蓄电池的许多功能。特别是新能源汽车的兴起,使得镍的作用得到更大程度的发挥和利用,比如永磁电机蓄电池制造。在A股市场中有17家企业,其中有从红土提镍
请回答1994柳传志向左,任正非向右,注定联想和华为今天的结局1994年,同年出生,同样是大器晚成的柳传志和任正非,相距千里,却同时秣马厉兵,进攻当时国产设备渗透率只有3。2的电信蓝海市场。华为的目标是找机会活下来,联想的目标是一战成名。原因
土耳其里拉暴跌苹果店停止销售最近,土耳其货币暴跌,正是捡漏的好时候,如果不是因为疫情,可能很多代购要飞去土耳其抢购奢侈品了。科技巨头苹果则反应迅速,为了防止被薅羊毛,已经停止了土耳其地区的产品销售。目前,苹果
武汉永久版雷神山医院即将竣工11月27日,记者从武汉市江夏区卫健委获悉,武汉市永久版雷神山医院江夏云景山医院主体结构已竣工,各项建设工作进入最后冲刺阶段,即将投入使用。武汉市江夏云景山医院由中建三局承建,是湖
李克强出席第十三届亚欧首脑会议非正式会议来源人民网人民日报海外版11月26日晚,国务院总理李克强在北京人民大会堂出席第十三届亚欧首脑会议非正式会议并发表讲话,就国际和地区问题同各国领导人交换意见,会议以视频方式举行。新华
外围股市大崩盘,新病毒席卷而来,下周A股能够独善其中吗?就在昨日,突发的黑天鹅侵袭了全球股市(除了A股),我们可以看到日本欧洲股市暴跌,原油崩5,美股三大指数狂泻2以上!那么,黑天鹅的主要原因就是南非新发现的病毒,B。1。1。529认定
简单干净的文案短句1温柔的鼓励和爱意是成长路上最好的恩赐2且听风吟静待花开3不要因为这世上有人跟你道不同,你就不好好走自己的路4你的心,是我想要到达的蔚蓝海岸。5就算星星碎掉了,溢出来的光也很好看。
不再心动被秒赞的文案集合了1比起忽冷忽热的陪伴孤独才让人踏实。2后来,连喜欢的人也没有发呆也不知道想谁。3我不强求任何一段美好,即使我很在意愿浅笑久伴。4不知道从什么时候开始,不再相信任何人。5所以什么才是
小米MIUI小爱同学App更名小爱语音,小爱同学由多款应用组成IT之家9月3日消息,昨日,小米MIUI推送小爱同学App更新,正式将小爱同学App更名为小爱语音。小米官方表示,小爱同学将由语音助手升级成个人智能助手,升级后的小爱同学将由小爱语
两大老牌工厂突然停业!转型不易图片来源图虫创意即将迈入外贸出口行业金九银十旺季,近期汇率与海运的双重回温让外贸行业再现活力,有不少外贸工厂重振旗鼓,准备接单,可是却也有一些外贸工厂在前期的亏损中难以支撑,倒在了
30004000价位手机推荐OPPOFindX5pro天玑版香喷喷的跳水冠军原价122565799现全网最低价(拼多多百亿补贴)122563589前言提起OPPOFindX系列,就不能不提2018年发布的第一代FindX,如果要用四个字形容,那一定是美得惊
浪漫而不烂俗的高级文案希望步伐一致的人可以早点相遇。当满树樱花全部退为绿叶,浪漫便藏起了花期。宇宙山河浪漫,人间点滴温暖,都值得我们前进。与理想平等交易,与喧嚣保持距离。想念就去见面,带着花和真诚,光说
随馨札记你的自律,终将美好作者随馨札记有人说人在年轻的时候,觉得到处都是人,别人的事就是你的事。到了中年以后,才觉得世界上除了家人,已经一无所有了。相信,你的自律,终将美好冰心说一个美好的家庭,乃是一切幸福
航天员和晨昏线同框,太美了近日,一张航天员和晨昏线同框的图片火遍全网。图片上是神州十四号乘组,陈冬在舱外执行任务。大家知道,北京时间,2022年6月5日10时44分,神州十四号飞行乘组三名航天员陈冬刘洋和蔡
全国油价调整信息9月3日调整后9295号汽油价格油价经历5连跌后,今年第11涨将在下周二晚开启,朋友们,请提前加油!2022年第17轮国内成品油调整将于(9月6日24时)开启,此前呈现了10涨,6跌走势,经过5连跌后,油价累计下
小米手机越做越倒退吗?机型为红米note11tpro,这手机续航倒也勉强可以,但音质和拍照绝对是垃圾级别的,外放和耳机还不如前些年的note系列,拍照和音质下降了好几个等级!破音散音严重,就算有所谓杜比
3K元手机怎么买?这四款买完绝不后悔3000元价位段手机一般配置都比较不错,但对于买手机而言除了看配置更要有优秀的使用体验,小编挑选了四款在配置和体验方面都还不错的产品推荐给大家,同时这些手机在京东也有不错优惠。iQ