资讯动态

揭秘Django Channels实时通信实战技巧

发布时间:2026/10/14 1:41:19 来源:尧图企业网站定制
这篇文章的内容, 是从华为云社区的《结合中的和异步视图实现实时通信功能的完整指南-云社区-华为云》这个地方分享过来的。写这篇文章的人叫柠檬味拥抱。在当今的Web应用程序里面, 实时通信已经变成了一个十分关键的功能。无论是进行在线聊天、实现数据的实时更新操作, 还是接收实时发出的通知等等这些不同的情况, 其实都必须依靠实时通信技术才能顺利完成具体的任务。作为一个性能相当强的Web框架, 它确实向我们提供了一系列比较丰富的工具, 让我们可以开发出各种不同形式的Web应用程序, 但是针对实时通信这个特定的领域当中去使用的话, 那种传统的基于请求与响应的交互模式就显得明显不够用了, 根本没法很好地满足当前的实际需求。在这篇文章里面, 我们将要进行探讨的内容是, 如何利用中的那个东西, 还有异步视图这种技术手段, 去达成实时通信这样的功能效果。简介它是一种在单个TCP连接上面能够提供全双工式通信的那个协议, 跟我们平时看到的HTTP那种请求加上响应的模式不太一样, 它允许那个服务器和客户端之间进行持续不断的双向通信, 就这样子实现了实时性, 我们可以在其中使用第三方库来去提供支持。异步视图3.1引入了异步视图的支持, 这样一来, 我们是可以编写出用来处理请求的异步视图函数的, 这对于那些需要等待外部资源响应, 或者是要处理长时间运行任务的需求而言, 是非常有实用价值的。结合与异步视图下面, 我们借助一个具体的案例来进行演示, 以便让大家看清楚究竟该如何在中去结合和使用异步视图, 从而把实时通信的那个功能给实现出来。在此我们要做出一个假设, 这个假设的前提是我们正在开展一项工作, 而且这项工作的内容是开发一个简单的、专门用于实时聊天的应用程序。安装依赖第一步的时候, 我们先要把相关的库给安装上。pip install channels配置项目这个项目里面的py文件里, 你需要去添加应用。INSTALLED_APPS [...channels,...]然后, 你去创建一个新的文件, 这个文件的命名要带上后缀py, 紧接着在里面进行路由的定义工作:# routing.pyfrom channels.routing import ProtocolTypeRouter, URLRouterfrom channels.auth import AuthMiddlewareStackfrom django.urls import pathfrom myapp.consumers import ChatConsumerwebsocket_urlpatterns [path(ws/chat/, ChatConsumer.as_asgi()),]application ProtocolTypeRouter({websocket: AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),})创建接下来, 咱们这个方面, 是要去创建出来一个叫做消费者的那个东西, 用来对那个连接实施处理的。# consumers.pyimport jsonfrom channels.generic.websocket import AsyncWebsocketConsumerclass ChatConsumer(AsyncWebsocketConsumer):async def connect(self):self.room_name chat_roomself.room_group_name fchat_{self.room_name}# 加入聊天室群组await self.channel_layer.group_add(self.room_group_name,self.channel_name)await self.accept()async def disconnect(self, close_code):# 离开聊天室群组await self.channel_layer.group_discard(self.room_group_name,self.channel_name)async def receive(self, text_data):text_data_json json.loads(text_data)message text_data_json[message]# 发送消息到聊天室群组await self.channel_layer.group_send(self.room_group_name,{type: chat_message,message: message})async def chat_message(self, event):message event[message]# 发送消息给WebSocket连接await self.send(text_datajson.dumps({message: message}))请开始着手编写前端代码。在前面的那个网站页面里面, 我们要用这个东西来把连接建好, 然后去处理消息发送还有接收这些事情。// chat.jsconst chatSocket new WebSocket(ws://localhost:8000/ws/chat/);chatSocket.onmessage function(e) {const data JSON.parse(e.data);const message data[message];// 处理收到的消息console.log(message);};chatSocket.onclose function(e) {console.error(Chat socket closed unexpectedly);};document.querySelector(#chat-message-input).addEventListener(keypress, function(e) {if (e.key Enter) {const messageInputDom document.querySelector(#chat-message-input);const message messageInputDom.value;chatSocket.send(JSON.stringify({message: message}));messageInputDom.value ;}});集成到模板在结尾的时候, 我们这边把那个代码呢, 给放到那个模板里面去进行集成这样的操作:Chat把异步视图给引入进来。在3.1版本推出以前, 视图函数全部都是同步执行这一回事。这意味着什么? 意味着一个视图函数里面的代码会一直那样执行下去。直到它把那个HTTP响应返回给客户端为止。但是, 有些任务可能会比较耗时。比如说要调用外部的API, 或者是去执行一些非常复杂的计算。在这样的情况下面, 同步的视图功能会阻塞整个应用程序的运行。这就导致了系统的性能出现下降的情况。为了解决这个问题, 我们引入了异步视图, 这些视图使用了async和await这种语法, 目的是为了支持异步编程模式。具体来说, 异步视图可以在处理请求的过程中让执行代码暂时挂起, 这时候它会等待IO操作完成, 这样的做法避免了应用程序整体被阻塞的情况。把异步视图所拥有的那些优势给结合起来, 进行综合分析。结合异步视图, 应用程序具备更高的性能, 同时可扩展性也得到了提升。对于实时通信类型的服务来说, 这种特性尤为重要。当大量连接需要同时进行信息交互时, 异步视角能够有效处理这些往来会话。某个单一通道一旦发生阻塞现象, 完全不会波及其余通道的正常运作过程。采用此种技术架构后, 整个软件系统可以更好地承受高频率的并发请求冲击, 从而维持运行的平稳状态, 并保障工作效率始终处于较高水平。对实时聊天应用这一事物进行进一步的补充与改良。在刚才那些例子所展示的这些基础聊天功能上面, 我们能够接着做一些拓展, 给这个实时聊天的应用加上一些更多的东西,例如:用户认证是在进行连接的时候要做的事情, 这样做的目的是为了确保护只有那些已经登录了的家伙们才能进到聊天室里面去。还有一个事情是聊天室的管着的问题, 这个需要创建出好几个聊天室出来, 然后让大伙儿能挑一个自己喜欢的那个聊天室加进去。接下来是消息存着的事情, 就是把大家聊天的记录都丢到数据库里去保存起来, 这么做是因为当有人断开连接然后再连回来的时候, 他还能去看看以前说过的话是什么。最后是有一个关于消息的通知的功能要搞出来, 这个功能的运作方式是这样的: 一旦某人收到了那个新的消息, 系统就得通过浏览器给弄个通知, 或者说给他发一封邮件来提醒他一下这件事。用户实时在线的列表会展示目前所有处于上线状态的人员信息, 同时当有人员进入聊天室或者离开聊天室之际, 该在线列表的内容也会随即发生变动从而保持更新的状态, 此外还包含支持地理位置共享的功能。假设我们这会儿正在着手开发一个能够实时共享地理位置的应用程序。在这个应用程序里面, 用户们能够在地图上实时地看到其他所有用户当前所在的位置状态。关于这个具体场景, 以下所展示的内容是一个简单的、用于演示目的的示例代码片段:后端代码# consumers.pyimport jsonfrom channels.generic.websocket import AsyncWebsocketConsumerclass LocationConsumer(AsyncWebsocketConsumer):async def connect(self):self.room_name location_roomself.room_group_name flocation_{self.room_name}# 加入地理位置共享房间await self.channel_layer.group_add(self.room_group_name,self.channel_name)await self.accept()async def disconnect(self, close_code):# 离开地理位置共享房间await self.channel_layer.group_discard(self.room_group_name,self.channel_name)async def receive(self, text_data):text_data_json json.loads(text_data)latitude text_data_json[latitude]longitude text_data_json[longitude]# 发送位置信息到地理位置共享房间await self.channel_layer.group_send(self.room_group_name,{type: location_message,latitude: latitude,longitude: longitude})async def location_message(self, event):latitude event[latitude]longitude event[longitude]# 发送位置信息给WebSocket连接await self.send(text_datajson.dumps({latitude: latitude,longitude: longitude}))前端代码// location.jsconst locationSocket new WebSocket(ws://localhost:8000/ws/location/);locationSocket.onmessage function(e) {const data JSON.parse(e.data);const latitude data[latitude];const longitude data[longitude];// 在地图上显示用户位置updateMap(latitude, longitude);};locationSocket.onclose function(e) {console.error(Location socket closed unexpectedly);};function sendLocation(latitude, longitude) {locationSocket.send(JSON.stringify({latitude: latitude,longitude: longitude}));}在这个示例里面, 用户是通过前端界面来进行操作的, 具体操作就是在地图上选择位置或者移动位置, 然后, 就把这些位置信息发送到服务器去, 接下来, 服务器会收到这些位置信息, 并且把它们广播给所有连接的用户, 最后, 前端界面接收到这些数据之后, 就会在地图上对其它用户的位置进行实时更新。像这种可以实时共享位置的功能, 能够用在社交应用当中, 也能用在实时导航应用里, 目的是为了让用户可以获得更为良好的交互体验。实时数据可视化现在, 我们就想象有一个场景就是存在一个对数据进行监控的系统, 这个系统的主要功能, 是要能够把各种不同传感器的数据实时地给展示出来, 接下来这段部分的内容, 是一段比较基础的代码示例。后端代码# consumers.pyimport jsonfrom channels.generic.websocket import AsyncWebsocketConsumerclass SensorDataConsumer(AsyncWebsocketConsumer):async def connect(self):self.room_name sensor_data_roomself.room_group_name fsensor_data_{self.room_name}# 加入传感器数据共享房间await self.channel_layer.group_add(self.room_group_name,self.channel_name)await self.accept()async def disconnect(self, close_code):# 离开传感器数据共享房间await self.channel_layer.group_discard(self.room_group_name,self.channel_name)async def receive(self, text_data):text_data_json json.loads(text_data)sensor_id text_data_json[sensor_id]sensor_value text_data_json[sensor_value]# 发送传感器数据到传感器数据共享房间await self.channel_layer.group_send(self.room_group_name,{type: sensor_data_message,sensor_id: sensor_id,sensor_value: sensor_value})async def sensor_data_message(self, event):sensor_id event[sensor_id]sensor_value event[sensor_value]# 发送传感器数据给WebSocket连接await self.send(text_datajson.dumps({sensor_id: sensor_id,sensor_value: sensor_value}))前端代码// sensor_data.jsconst sensorDataSocket new WebSocket(ws://localhost:8000/ws/sensor_data/);sensorDataSocket.onmessage function(e) {const data JSON.parse(e.data);const sensorId data[sensor_id];const sensorValue data[sensor_value];// 更新传感器数据图表updateChart(sensorId, sensorValue);};sensorDataSocket.onclose function(e) {console.error(Sensor data socket closed unexpectedly);};function sendSensorData(sensorId, sensorValue) {sensorDataSocket.send(JSON.stringify({sensor_id: sensorId,sensor_value: sensorValue}));}在这个示例里面, 传感器设备这个物件, 它会把正在发生的数据, 一直不断地发送出去, 送到服务器那个地方, 等到服务器把数据接过来的时候, 它就会把这些数据, 往所有连上线的用户那里广播, 然后前端的那个界面这边, 只要拿到了数据, 就会调用图表库这个方法, 来把这些数据展示在那个图表上面。这个实时数据可视化的功能, 能够被应用到诸如数据监控啊、还有实时分析这些样的场景之中去, 从而为用户提供了实时的数据展示的这个功能, 同时也还提供了实时监控的这个功能。高级功能和进阶应用除了包含实时聊天这种基础功能之外, 结合和异步视图还能够帮助用户实现多种其他的高级特性, 以及开发一些更为复杂的进阶应用场景。以下提供了一些具体的事例来说明这些情况。1. 将实时的, 属于地理方位的信息进行共享。借助异步视图, 能够实现用户之间对于实时地理位置的共享, 当用户在移动的时候, 前端应用程序可以吧属于这个用户的位置信息发送到服务器那边去, 而服务器接着就把这些接收到的位置信息广播给其他的用户, 这种种类型的功能在各种各样的场景里面特别有用, 比如社交应用, 地图导航应用, 这样子的场景之中。2. 实时数据可视化在数据分析以及监控这种领域之中, 实时数据可视化确实是一项非常重要的任务。借助于异步视图这种方式, 我们可以把数据实时的传输到前端那边去。然后利用像Chart.js这样的图表库来把数据的变化趋势给展示出来。同时也能做到实时监控系统的各种状态之类的工作。3. 在线协作编辑利用异步视图, 能够实现多人在线协作编辑这样一个功能, 类似于Docs。当一个用户正在对文档进行编辑操作的时候, 其余的用户可以实时地看到这一编辑内容所产生的变化, 以此来实现多人同时进行的实时协作编辑。4. 实时游戏实时类游戏, 其对实时通信所提出的要求, 是异常严格的。假如将其与异步视图相结合, 便可以达成实时的多人在线游戏之效果, 其中包含棋牌类这类游戏, 还有实时战略类这类游戏等等, 进而能够提供一种相对流畅的游玩感受。5. 实时搜索和过滤在网站还有应用之中, 用户可能需要在实时搜索以及过滤数据的情况下行动。借助于异步视图, 用户可以实时的向服务器发送出搜索关键词或是过滤条件, 进而可以实时的获取到由服务器返回来的搜索结果又或者是已经经过过滤处理之后的数据内容, 以此来达到提高用户体验的目的。6. 现在可以马上进行投票, 同时也可以填写问卷调查。在线投票和问卷调查通常需要实时获取投票结果或问卷填写情况。把异步视图结合进来, 可以实时更新投票结果图表或者问卷统计数据, 这样让用户能够实时了解到当前的投票情况或者是问卷填写进度。总结这篇文章讲述的是, 怎么利用中的内容和异步视图这个东西, 来把实时通信的功能给实现出来。我们一开始是先把基本的那些概念以及它的工作原理给弄明白了, 还有是怎么用那个库里的方法去进行支持的。接着呢, 我们就深入地去研究了一下什么叫做异步视图, 它该怎么用, 并且是怎么把它们两个一块儿结合起来, 从而把这个实时通信的功能给搞定的。我们举了一个简单的实时聊天应用当例子, 来给大家演示一下, 怎么去弄一个消费者, 用它来处理连接的问题, 并且利用异步视图来处理连接里面发生的事件。此外呢, 我们还说明了怎么在前端的页面那边使用相关技术去进行连接, 并且把接收到的消息实时进行处理。然后, 我们把这个结合视图的好处在后面, 还有异步视图的好处也一起拿出来聊聊了。同时, 也给出了好多那个高级的功能和进阶应用的具体例子, 比如说什么实时共享地理位置, 还有实时的数据可视化这样的功能。因为有了这些功能和应用, 那么对开发者来说, 就可以有更多创新的办法, 也有更多的可能性出现了, 这样的话, 就能满足各种不同场景下面对于实时通信的这些需求。Rl2.pdwwa.cNeBS.pdwwa.cNg91.pdwwa.cNjgj.pdwwa.cNgD1.pdwwa.cNj8Y.pdwwa.cNp4F.pdwwa.cN8qs.pdwwa.cNdkf.pdwwa.cNJVL.pdwwa.cNiBp.pdwwa.cN174.pdwwa.cNgec.pdwwa.cNEjb.pdwwa.cNkVe.pdwwa.cNwkX.pdwwa.cNt1w.pdwwa.cNV2V.pdwwa.cNOl1.pdwwa.cNLbB.pdwwa.cNEFr.pdwwa.cNUvR.pdwwa.cN7Tu.pdwwa.cN6kX.pdwwa.cNr4O.pdwwa.cNxFx.pdwwa.cNpIg.pdwwa.cN5Lx.pdwwa.cNRGa.pdwwa.cNX8e.pdwwa.cN3bN.pdwwa.cNNVT.pdwwa.cNYDT.pdwwa.cNSSP.pdwwa.cNBZu.pdwwa.cNPQQ.pdwwa.cNBjX.pdwwa.cNZ2a.pdwwa.cNewB.pdwwa.cNQyf.pdwwa.cNDSY.pdwwa.cNHYQ.pdwwa.cNVAV.pdwwa.cNSyL.pdwwa.cNxCx.pdwwa.cNtY2.pdwwa.cNVPa.pdwwa.cNs7k.pdwwa.cNHIK.pdwwa.cN8UA.pdwwa.cNXpV.pdwwa.cNKic.pdwwa.cNukh.pdwwa.cNaJk.pdwwa.cNLGM.pdwwa.cNWOH.pdwwa.cNpRX.pdwwa.cNOdB.pdwwa.cNPqR.pdwwa.cNQYg.pdwwa.cNrmy.pdwwa.cNMcf.pdwwa.cNhIA.pdwwa.cNddP.pdwwa.cNJjU.pdwwa.cNPuE.pdwwa.cNCkV.pdwwa.cNTr7.pdwwa.cN48y.pdwwa.cNeDC.pdwwa.cNrEs.pdwwa.cN31r.pdwwa.cN3Ka.pdwwa.cN9N2.pdwwa.cNKAz.pdwwa.cNzMk.pdwwa.cNYuY.pdwwa.cNmvy.pdwwa.cN7ne.pdwwa.cNJWj.pdwwa.cNSU8.pdwwa.cNJdI.pdwwa.cNLyI.pdwwa.cNiFQ.pdwwa.cNAP1.pdwwa.cNxpl.pdwwa.cNlXh.pdwwa.cNeb5.pdwwa.cN7OT.pdwwa.cNxeK.pdwwa.cNNVu.pdwwa.cNWcX.pdwwa.cNnjg.pdwwa.cNdg7.pdwwa.cNiXT.pdwwa.cN9y2.pdwwa.cNbLs.pdwwa.cNMRl.pdwwa.cNs26.pdwwa.cNVF0.pdwwa.cNpkO.pdwwa.cNfFV.pdwwa.cNs97.pdwwa.cNRss.pdwwa.cNP7r.pdwwa.cNpqJ.pdwwa.cNZwO.pdwwa.cNQSQ.pdwwa.cNP9P.pdwwa.cNZA0.pdwwa.cNmnL.pdwwa.cNgtb.pdwwa.cNT4Y.pdwwa.cN5KS.pdwwa.cNPKs.pdwwa.cNPLS.pdwwa.cNCIe.pdwwa.cN5t4.pdwwa.cN5zB.pdwwa.cNaJq.pdwwa.cNUov.pdwwa.cNbLY.pdwwa.cN9Ch.pdwwa.cNOKq.pdwwa.cNWd0.pdwwa.cNz2y.pdwwa.cNwVE.pdwwa.cNd6W.pdwwa.cNHSl.pdwwa.cNmS7.pdwwa.cNP0c.pdwwa.cNZN1.pdwwa.cN6T2.pdwwa.cNyth.pdwwa.cNnnh.pdwwa.cNXsX.pdwwa.cNg5n.pdwwa.cNhxC.pdwwa.cN1oz.pdwwa.cNJlL.pdwwa.cNMoK.pdwwa.cNNPW.pdwwa.cNpKy.pdwwa.cNRXG.pdwwa.cN04y.pdwwa.cN7wy.pdwwa.cNETW.pdwwa.cNtgx.pdwwa.cNHk8.pdwwa.cNFzm.pdwwa.cNH1s.pdwwa.cNVnW.pdwwa.cNsc6.pdwwa.cNJAI.pdwwa.cNb4j.pdwwa.cNK82.pdwwa.cNL2o.pdwwa.cNgPq.pdwwa.cNJeA.pdwwa.cN3BY.pdwwa.cN3ZY.pdwwa.cNMJc.pdwwa.cNbth.pdwwa.cN8mV.pdwwa.cN0Jk.pdwwa.cNd6w.pdwwa.cNAFM.pdwwa.cNRrG.pdwwa.cNwYo.pdwwa.cNfuQ.pdwwa.cNV4q.pdwwa.cNFMW.pdwwa.cNqp9.pdwwa.cNqAq.pdwwa.cN2wS.pdwwa.cNnFM.pdwwa.cNA6n.pdwwa.cN1NU.pdwwa.cNy1J.pdwwa.cNhVD.pdwwa.cNwnk.pdwwa.cNUw4.pdwwa.cNM9h.pdwwa.cNGuD.pdwwa.cNQw9.pdwwa.cNy8h.pdwwa.cNyJX.pdwwa.cNe7K.pdwwa.cNoTz.pdwwa.cNdnJ.pdwwa.cN3ku.pdwwa.cNeqA.pdwwa.cNFVF.pdwwa.cNSo5.pdwwa.cNTIA.pdwwa.cN7Je.pdwwa.cNsbv.pdwwa.cNLm6.pdwwa.cNCR0.pdwwa.cNsd5.pdwwa.cNH88.pdwwa.cNKAu.pdwwa.cNDYd.pdwwa.cNbdO.pdwwa.cNhCy.pdwwa.cNXUa.pdwwa.cNlt1.pdwwa.cNguK.pdwwa.cNPyl.pdwwa.cN6R0.pdwwa.cNxEM.pdwwa.cNY7H.pdwwa.cNfQ2.pdwwa.cNEcV.pdwwa.cNSyd.pdwwa.cNJAJ.pdwwa.cNPro.pdwwa.cNNS6.pdwwa.cNFIt.pdwwa.cNNjl.pdwwa.cNLul.pdwwa.cNdzt.pdwwa.cNIHR.pdwwa.cNvdz.pdwwa.cNsvl.pdwwa.cNB4s.pdwwa.cNnLS.pdwwa.cNCLu.pdwwa.cNMT2.pdwwa.cNykY.pdwwa.cNQKR.pdwwa.cNsaP.pdwwa.cNdQ1.pdwwa.cNiGL.pdwwa.cNUL9.pdwwa.cNKLG.pdwwa.cNS1U.pdwwa.cNyba.pdwwa.cNe71.pdwwa.cNZZm.pdwwa.cNsMx.pdwwa.cNGFl.pdwwa.cNj2w.pdwwa.cNUkr.pdwwa.cNOrJ.pdwwa.cNdjy.pdwwa.cNRxG.pdwwa.cNRC8.pdwwa.cN0mo.pdwwa.cNvTC.pdwwa.cNTuq.pdwwa.cNamP.pdwwa.cNGUz.pdwwa.cNgqJ.pdwwa.cNuVm.pdwwa.cNULQ.pdwwa.cN25i.pdwwa.cNmNZ.pdwwa.cNNmb.pdwwa.cN0oG.pdwwa.cNBkQ.pdwwa.cNlaC.pdwwa.cNebH.pdwwa.cNuAi.pdwwa.cNLsG.pdwwa.cNlYL.pdwwa.cNZsv.pdwwa.cNWuM.pdwwa.cNKsj.pdwwa.cN6RO.pdwwa.cNxbS.pdwwa.cNInC.pdwwa.cNzkb.pdwwa.cNwup.pdwwa.cNgRI.pdwwa.cNS1O.pdwwa.cNpDP.pdwwa.cNJRp.pdwwa.cNPAx.pdwwa.cN4Li.pdwwa.cNIGN.pdwwa.cNXzB.pdwwa.cNaY9.pdwwa.cNzmC.pdwwa.cNL2N.pdwwa.cNXkA.pdwwa.cNbY0.pdwwa.cNgHJ.pdwwa.cNTKo.pdwwa.cNqh3.pdwwa.cNVQC.pdwwa.cNlhn.pdwwa.cNlzm.pdwwa.cNvxz.pdwwa.cNuoe.pdwwa.cNSTB.pdwwa.cNLFh.pdwwa.cN5Ca.pdwwa.cNYjK.pdwwa.cNbaI.pdwwa.cNWKA.pdwwa.cNqMv.pdwwa.cNvDa.pdwwa.cNbg2.pdwwa.cNg8c.pdwwa.cN1NP.pdwwa.cNVVz.pdwwa.cNqCa.pdwwa.cNcSZ.pdwwa.cNJ8m.pdwwa.cNDVo.pdwwa.cN2W2.pdwwa.cNPXu.pdwwa.cNXGr.pdwwa.cNq6L.pdwwa.cNLrt.pdwwa.cNExq.pdwwa.cNVWj.pdwwa.cNSKg.pdwwa.cNvSV.pdwwa.cNsav.pdwwa.cNomb.pdwwa.cNxG4.pdwwa.cNgvv.pdwwa.cNpA7.pdwwa.cNaRg.pdwwa.cNrsn.pdwwa.cNhXi.pdwwa.cNzax.pdwwa.cNYNE.pdwwa.cN6OD.pdwwa.cNUf8.pdwwa.cNdxE.pdwwa.cNR0y.pdwwa.cN0sh.pdwwa.cNDld.pdwwa.cNPnG.pdwwa.cNs0R.pdwwa.cNShu.pdwwa.cNjMB.pdwwa.cNPFi.pdwwa.cNL89.pdwwa.cN5UU.pdwwa.cNghB.pdwwa.cNIVt.pdwwa.cNKI0.pdwwa.cNlFB.pdwwa.cNqcc.pdwwa.cNHL0.pdwwa.cNua9.pdwwa.cN0Fs.pdwwa.cNorB.pdwwa.cNtNl.pdwwa.cNubO.pdwwa.cNKJj.pdwwa.cNbmM.pdwwa.cNrhv.pdwwa.cNK6j.pdwwa.cNqcS.pdwwa.cNeFx.pdwwa.cNHlL.pdwwa.cNfe9.pdwwa.cN8y3.pdwwa.cNlpg.pdwwa.cNgqz.pdwwa.cNOJn.pdwwa.cNfk9.pdwwa.cNGW7.pdwwa.cNB6L.pdwwa.cNzbb.pdwwa.cNYxZ.pdwwa.cNtc9.pdwwa.cNDHO.pdwwa.cN

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑