Python实战案例:tornado接口vue客户端的堂食点餐系统(中)

Python实战案例:tornado接口vue客户端的堂食点餐系统(中)

用Vue+ElementUI技术完成的堂食点餐系统的界面已经搭建成功,但这里面有一个问题,当用户点击其中某一个商品的时候,对应的商品和金额就要去更新底部购物车信息区。这就需要vue的methods方法的设定

一、Vue实现购买商品信息的更新

在vue中,函数被定义成为方法来使用,这些方法定义在methods属性中,然后就可以在vue 表达式中调用函数。

vue 选项对象中有一个叫methods的属性.这个属性里面专门来存放一些函数,用来给别人调用。这个methods属性也是写在script中,与data()并驾齐趋的。格式如下。

export  default{
    data(){
        return{
        }
    },
    methods:{
        方法名()
    }
}

methods这个方法发生的条件是餐饮列表中“添加”这一项能够被选中。在目前的布局中,是没有添加一个商品的按钮的,我们把布局文件中按钮用ElementUI中的“+”号图标来表示添加按钮,这样就需要把布局做一下修改,同时把“+”按钮对应的动作设定为methods中的方法,在这个方法里暂时实现添加一个商品的数量,每点击一次添加一个商品,在data()中定义初始化的商品数量,在底部购物车信息中显示具体的商品数量。代码如下。

<template>
  <div >
      <el-header >
        绝妙订餐系统
      </el-header>
       <el-row>
           <el-col :span="4">
              <el-menu
                background-color="darkgreen"
                text-color="#fff"
               >
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-platform"></i>
                            <span>厨师推荐</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-tools"></i>
                            <span>精品凉菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-user-solid"></i>
                              <span>特色小炒</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>家常热菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>营养主食</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>调味饮料</span>
                        </template>
                    </el-menu-item>
              </el-menu>
          </el-col>
         <el-col :span="20">
              <el-table :data="foodlist" height="330">

                  <el-table-column label="菜名"  prop="name"></el-table-column>
                  <el-table-column label="菜品图片"  prop="img">
                        <template slot-scope="scope">
                          <img :src="scope.row.img" width="170" height="124"/>
                        </template>
                  </el-table-column>
                  <el-table-column label="菜品价格"  prop="price"></el-table-column>
                  <el-table-column label="操作" >
                       <i class="el-icon-circle-plus" @click="plus_goods()"></i>
                                      </el-table-column>
              </el-table>
         </el-col>

      </el-row>
      <el-row>
            <el-col :span="2">
              <div class="bg-green-dark">
                  <i class="el-icon-cart"></i>
              </div>
            </el-col>
            <el-col :span="18">
              <div class="bg-green-dark">
                商品{{num_food}}件,0元
              </div>
            </el-col>
            <el-col :span="4">
              <div class="bg-red-dark">
                去结算
              </div>
            </el-col>
      </el-row>

  </div>
</template>

<script>
    import "../assets/logo.png"
    import axios from "axios"
    export default {
       name: 'OrderFood',
       data(){
              return {
                    num_food:0,
                  /*定义全局booklist*/
                    foodlist:[{
                    "name":"野蘑菇炖鸡",
                    "img":"./static/images/mogu_ji.png",
                    "price":58.00
                  },{
                    "name":"大盘鸡",
                    "img":"./static/images/dapan_ji.png",
                    "price":62.00
                  },
                  {
                    "name":"爆椒鸡",
                    "img":"./static/images/baojiao_ji.png",
                    "price":48.00
                  },{
                    "name":"辣子鸡丁",
                    "img":"./static/images/lazi_ji.png",
                    "price":39.00
                  },
                  {
                    "name":"麻辣鱼头",
                    "img":"./static/images/mala_yu.png",
                    "price":52.00
                  },{
                    "name":"酸菜鱼",
                    "img":"./static/images/lazi_ji.png",
                    "price":53.00
                  },
                  {
                    "name":"红烧带鱼",
                    "img":"./static/images/mala_yu.png",
                    "price":42.00
                  },{
                    "name":"水煮肉片",
                    "img":"./static/images/dapan_ji.png",
                    "price":35.00
                  },
                  {
                    "name":"毛血旺",
                    "img":"./static/images/baojiao_ji.png",
                    "price":30.00
                  },{
                    "name":"鱼香肉丝",
                    "img":"./static/images/lazi_ji.png",
                    "price":18.00
                  },
                  {
                    "name":"烧大肠",
                    "img":"./static/images/mala_yu.png",
                    "price":28.00
                  },{
                    "name":"农家小炒肉",
                    "img":"./static/images/lazi_ji.png",
                    "price":25.00
                  },
                  {
                    "name":"青椒肉丝",
                    "img":"./static/images/mala_yu.png",
                    "price":20.00
                  },{
                    "name":"京酱肉丝",
                    "img":"./static/images/mala_yu.png",
                    "price":19.00
                  },{
                    "name":"红烧肉(带饼)",
                    "img":"./static/images/lazi_ji.png",
                    "price":30.00
                  },
                  {
                    "name":"回锅肉",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"木须肉",
                    "img":"./static/images/mala_yu.png",
                    "price":18.00
                  },{
                    "name":"酸豆角炒肉末",
                    "img":"./static/images/lazi_ji.png",
                    "price":22.00
                  },
                  {
                    "name":"鲜辣烤鱼",
                    "img":"./static/images/mala_yu.png",
                    "price":79.00
                  },{
                    "name":"干锅菜花",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"干锅千页豆腐",
                    "img":"./static/images/lazi_ji.png",
                    "price":15.00
                  },
                  {
                    "name":"西红柿烧牛楠",
                    "img":"./static/images/mala_yu.png",
                    "price":29.00
                  },{
                    "name":"绿豆牙炒馓子",
                    "img":"./static/images/mala_yu.png",
                    "price":13.00
                  },{
                    "name":"西红柿炒鸡蛋",
                    "img":"./static/images/lazi_ji.png",
                    "price":12.00
                  },
                  {
                    "name":"酸辣土豆丝",
                    "img":"./static/images/mala_yu.png",
                    "price":14.00
                  },{
                    "name":"香菇青菜",
                    "img":"./static/images/mala_yu.png",
                    "price":17.00
                  },{
                    "name":"麻辣豆腐",
                    "img":"./static/images/lazi_ji.png",
                    "price":10.00
                  },
                  {
                    "name":"鱼香茄子",
                    "img":"./static/images/mala_yu.png",
                    "price":15.00
                  },{
                    "name":"爆炒花哈",
                    "img":"./static/images/mala_yu.png",
                    "price":23.00
                  },{
                    "name":"豆芽粉条",
                    "img":"./static/images/lazi_ji.png",
                    "price":11.00
                  },
                  {
                    "name":"红油耳丝",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"变蛋黄豆",
                    "img":"./static/images/mala_yu.png",
                    "price":9.00
                  },{
                    "name":"麻辣花生米",
                    "img":"./static/images/lazi_ji.png",
                    "price":9.00
                  },
                  {
                    "name":"水蒸蛋",
                    "img":"./static/images/mala_yu.png",
                    "price":10.00
                  },{
                    "name":"烧茄子",
                    "img":"./static/images/mala_yu.png",
                    "price":14.00
                  },{
                    "name":"炒饼丝",
                    "img":"./static/images/lazi_ji.png",
                    "price":8.00
                  }]
              }
       },
      methods:{
          plus_goods(){
              this.num_food++;
                    }
      }
    }
</script>


<style scoped>
      .el-header{
        background-color:darkgreen;
        color:white;
        line-height:60px;
        font-size:20px;
      }
      .el-menu-item:hover{
          background:yellow!important;
          color:purple!important;

      }
      .el-menu-item.is-active{
          background:purple!important;
          color:white!important;

      }
      .el-icon-cart{
         width:60px;
         height:60px;
         background: url("../assets/image/cart.png") center no-repeat;
         background-size:100% 100%;
         margin-top:-10px;
      }
      .el-icon-cart:before{
        visibility: hidden;
      }
      .bg-green-dark{
        background-color:black;
        height:60px;
        line-height:60px;
        color:white;
      }
      .bg-red-dark{
        background-color:darkred;
        color:white;
        height:60px;
        line-height:60px;
      }
</style>

从代码上看,在右侧商品列表区的<el-table>又多出了<el-table-column>,在这个<el-table-column>中加入了一个i标签,类别是el-icon-circle-plus,并且在这个i标签上添加@click属性,对应methods中的方法plusgoods函数,函数体中的语句使用this.numfoods+=1实现商品数量的添加。效果图如下图所示。

从图中效果可以看到,当点击点餐区中的“+”号按钮时,底部购物车状态信息中的商品就会一件一件的添加。接下来要实现的就是商品总价格的更新。这需要寻找“+”号前面的“菜品价格”维度中的值,对于表格中调用每一行中的数值可以使用scope来实现,这就需要模板template中使用属性slot-scope来使模板内部的元素对scope的使用。这样可以在调用methods里面方法的时候传入scope的数据。当把scope数据传送进plusgoods函数中时,函数接收对应的参数,并把scope中的$index索引获取出来,就可以取出对应索引的“菜品价格”数据,每点击一次“+”号添加按钮,就添加一个商品,对应的“商品价格”数据就会累加到商品总价格中。data()数据初始化方法中也需要定义商品求和的初值为0,在plusgoods中实现商品和值的累加。代码如下。

<template>
  <div >
      <el-header >
        绝妙订餐系统
      </el-header>
       <el-row>
           <el-col :span="4">
              <el-menu
                background-color="darkgreen"
                text-color="#fff"
               >
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-platform"></i>
                            <span>厨师推荐</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-tools"></i>
                            <span>精品凉菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-user-solid"></i>
                              <span>特色小炒</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>家常热菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>营养主食</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>调味饮料</span>
                        </template>
                    </el-menu-item>
              </el-menu>
          </el-col>
         <el-col :span="20">
              <el-table :data="foodlist" height="330">

                  <el-table-column label="菜名"  prop="name"></el-table-column>
                  <el-table-column label="菜品图片"  prop="img">
                        <template slot-scope="scope">
                          <img :src="scope.row.img" width="170" height="124"/>
                        </template>
                  </el-table-column>
                  <el-table-column label="菜品价格"  prop="price"></el-table-column>
                  <el-table-column label="操作" >
                       <template slot-scope="scope" >
    <i class="el-icon-circle-plus" @click="plus_goods({scope})"></i>
</template>
                                      </el-table-column>
              </el-table>
         </el-col>

      </el-row>
      <el-row>
            <el-col :span="2">
              <div class="bg-green-dark">
                  <i class="el-icon-cart"></i>
              </div>
            </el-col>
            <el-col :span="18">
              <div class="bg-green-dark">
                商品{{num_food}}件,{{num_price}}元
              </div>
            </el-col>
            <el-col :span="4">
              <div class="bg-red-dark">
                去结算
              </div>
            </el-col>
      </el-row>

  </div>
</template>

<script>
  import "../assets/logo.png"
  import axios from "axios"
    export default {
       name: 'OrderFood',
       data(){
              return {
                    num_food:0,
                    num_price:0,
                  /*定义全局booklist*/
                    foodlist:[{
                    "name":"野蘑菇炖鸡",
                    "img":"./static/images/mogu_ji.png",
                    "price":58.00
                  },{
                    "name":"大盘鸡",
                    "img":"./static/images/dapan_ji.png",
                    "price":62.00
                  },
                  {
                    "name":"爆椒鸡",
                    "img":"./static/images/baojiao_ji.png",
                    "price":48.00
                  },{
                    "name":"辣子鸡丁",
                    "img":"./static/images/lazi_ji.png",
                    "price":39.00
                  },
                  {
                    "name":"麻辣鱼头",
                    "img":"./static/images/mala_yu.png",
                    "price":52.00
                  },{
                    "name":"酸菜鱼",
                    "img":"./static/images/lazi_ji.png",
                    "price":53.00
                  },
                  {
                    "name":"红烧带鱼",
                    "img":"./static/images/mala_yu.png",
                    "price":42.00
                  },{
                    "name":"水煮肉片",
                    "img":"./static/images/dapan_ji.png",
                    "price":35.00
                  },
                  {
                    "name":"毛血旺",
                    "img":"./static/images/baojiao_ji.png",
                    "price":30.00
                  },{
                    "name":"鱼香肉丝",
                    "img":"./static/images/lazi_ji.png",
                    "price":18.00
                  },
                  {
                    "name":"烧大肠",
                    "img":"./static/images/mala_yu.png",
                    "price":28.00
                  },{
                    "name":"农家小炒肉",
                    "img":"./static/images/lazi_ji.png",
                    "price":25.00
                  },
                  {
                    "name":"青椒肉丝",
                    "img":"./static/images/mala_yu.png",
                    "price":20.00
                  },{
                    "name":"京酱肉丝",
                    "img":"./static/images/mala_yu.png",
                    "price":19.00
                  },{
                    "name":"红烧肉(带饼)",
                    "img":"./static/images/lazi_ji.png",
                    "price":30.00
                  },
                  {
                    "name":"回锅肉",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"木须肉",
                    "img":"./static/images/mala_yu.png",
                    "price":18.00
                  },{
                    "name":"酸豆角炒肉末",
                    "img":"./static/images/lazi_ji.png",
                    "price":22.00
                  },
                  {
                    "name":"鲜辣烤鱼",
                    "img":"./static/images/mala_yu.png",
                    "price":79.00
                  },{
                    "name":"干锅菜花",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"干锅千页豆腐",
                    "img":"./static/images/lazi_ji.png",
                    "price":15.00
                  },
                  {
                    "name":"西红柿烧牛楠",
                    "img":"./static/images/mala_yu.png",
                    "price":29.00
                  },{
                    "name":"绿豆牙炒馓子",
                    "img":"./static/images/mala_yu.png",
                    "price":13.00
                  },{
                    "name":"西红柿炒鸡蛋",
                    "img":"./static/images/lazi_ji.png",
                    "price":12.00
                  },
                  {
                    "name":"酸辣土豆丝",
                    "img":"./static/images/mala_yu.png",
                    "price":14.00
                  },{
                    "name":"香菇青菜",
                    "img":"./static/images/mala_yu.png",
                    "price":17.00
                  },{
                    "name":"麻辣豆腐",
                    "img":"./static/images/lazi_ji.png",
                    "price":10.00
                  },
                  {
                    "name":"鱼香茄子",
                    "img":"./static/images/mala_yu.png",
                    "price":15.00
                  },{
                    "name":"爆炒花哈",
                    "img":"./static/images/mala_yu.png",
                    "price":23.00
                  },{
                    "name":"豆芽粉条",
                    "img":"./static/images/lazi_ji.png",
                    "price":11.00
                  },
                  {
                    "name":"红油耳丝",
                    "img":"./static/images/mala_yu.png",
                    "price":16.00
                  },{
                    "name":"变蛋黄豆",
                    "img":"./static/images/mala_yu.png",
                    "price":9.00
                  },{
                    "name":"麻辣花生米",
                    "img":"./static/images/lazi_ji.png",
                    "price":9.00
                  },
                  {
                    "name":"水蒸蛋",
                    "img":"./static/images/mala_yu.png",
                    "price":10.00
                  },{
                    "name":"烧茄子",
                    "img":"./static/images/mala_yu.png",
                    "price":14.00
                  },{
                    "name":"炒饼丝",
                    "img":"./static/images/lazi_ji.png",
                    "price":8.00
                  }]
              }
       },
      methods:{
          plus_goods(datas){
        this.num_food++;
        console.log(this.foodlist[datas.scope.$index]["price"])
        this.num_price+=this.foodlist[datas.scope.$index]["price"];
        console.log(this.num_price)

        }
     }
    }
</script>


<style scoped>
      .el-header{
        background-color:darkgreen;
        color:white;
        line-height:60px;
        font-size:20px;
      }
      .el-menu-item:hover{
          background:yellow!important;
          color:purple!important;

      }
      .el-menu-item.is-active{
          background:purple!important;
          color:white!important;

      }
      .el-icon-cart{
         width:60px;
         height:60px;
         background: url("../assets/image/cart.png") center no-repeat;
         background-size:100% 100%;
         margin-top:-10px;
      }
      .el-icon-cart:before{
        visibility: hidden;
      }
      .bg-green-dark{
        background-color:black;
        height:60px;
        line-height:60px;
        color:white;
      }
      .bg-red-dark{
        background-color:darkred;
        color:white;
        height:60px;
        line-height:60px;
      }
</style>

代码最终执行结果如下图所示。

二、tornado框架的认识

Tornado是一种 Web 服务器软件的开源版本。Tornado 和主流Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个 理想框架。

像web的框架中,django这类框架,采用WSGI协议与服务器对接的,而这类服务器通常是基于多线程/多进程的,也就是说每有一个网络请求,服务器都会有一个线程/进程进行处理。

遇到高并发的场景就不容易处理,高并发场景情况有以下两种:

1.用户量大,高并发。如秒杀抢购,双十一,618和春节抢票。

2.大量的HTTP持久连接。

基于上述高并发场景,引出了C10k问题,是由一名叫DanKegel的软件工程师提出的,即当同时的连接数以万计的时候,服务器性能会出现急剧下降甚至直接崩溃的情况,这就是著名的C10k问题。

值得一提的,腾讯QQ就遇到过C10k问题,当时采用了udp的方式避开了这个问题,当然过程是相当痛苦的,后来也就专用了tcp,主要是当时还没有epoll技术。

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

define _FDSETSIZE 1024

表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

如何解决c10k问题:

解决一、对于每个连接处理分配一个独立的进程/线程。提升单台机器的能力,尽可能多提供进程/线程,一台机器不够就增加多台机器。

解决二、用一个进程/线程来同时处理若干个连接。

针对方法一,假设每台机器都达到了一万连接,同时有一亿个请求,那么就需要一万台机器,所以这种解决方法不太实际。

针对方法二,需要有新的技术支持这种方案,实际上是可行的,也是现在普遍采取的方法,针对这种方法,tornado采用epoll技术。

Tornado采用了epoll技术,通过这种技术也就解决了著名的C10k问题,实现了用一个进程/线程来同时处理若干个连接的想法,减少了硬件资源的浪费。

总的来说, 一个tornado服务器可以分为四层, 工作流程大致是下面这样:

三、tornado框架Hello World的实现

tornado框架需要调用基本的三个模块:

tornado.web,tornado.ioloop,tornado.httpserver。

通过tornado框架实现Hello world的代码如下。

import tornado.httpserver
import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):

    def get(self):
        self.write('hello world')


if __name__ == '__main__':
    application = tornado.web.Application(
        handlers=[
            (r'/', MainHandler)
        ]
    )
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8001)
    tornado.ioloop.IOLoop.instance().start()

代码中首先定义一个类MainHandler,这是处理用户请求的类,也就是一个地址对应一个请求的处理类,这个类需要继承于tornado.web.RequestHandler,这是tornado中实现封装好的处理http请求的基类。在这个基类中有get和post方法,其实对应处理的就是前端页面提交到服务器的post或get方式。在这段代码中,实现了一个get请求的方法,get中的参数是类方法所具备的self参数,在函数体中直接在网页上输出“hello World”,采用self.write方法。

在主程序调用方面,首先定义一个application应用程序,代码上直接实例化tornado.web.Application,调用了tornado框架封装好的Application的应用,在其中定义参数handlers,其实就是实现地址与实现类的映射关系,这里的地址使用正则表达式进行匹配,实现类就是在代码最开始定义的类MainHandler。

接下来实例化tornado.httpserver中的HTTPServer类,传入参数application的应用,这是httpserver服务器的启动类,然后监听这个tornado程序的8001端口,也就是web服务器的端口访问号。ioloop是tornado的关键,是他的最底层。ioloop就是对I/O多路复用的封装,它实现了一个单例,调用instance()单例的start()方法就实现了启动这个web服务。最后,可以在浏览器中输入http://localhost:8001进行访问。

运行效果如下图所示。

四、tornado框架实现json接口

通过访问http://localhost:8001可以在网页中写入“hello word”,那也可以写入json数据,这样tornado框架就相当于返回了前端一个接口数据。

我们处理的是Vue堂食点餐系统,数据一定是后端传输到前端所获取的。现在将tornado框架的hello world程序做一些修改,使其返回前端需要的foodlist这个餐饮列表数据,这样就相当于实现了餐饮列表数据的接口。代码如下。

import tornado.httpserver
import tornado.ioloop
import tornado.web
import json

class MainHandler(tornado.web.RequestHandler):

    def get(self):
        foods_json = [{
                "name": "野蘑菇炖鸡",
                "img": "./static/images/mogu_ji.png",
                "price": 58.00
            }, {
                "name": "大盘鸡",
                "img": "./static/images/dapan_ji.png",
                "price": 62.00
            },
            {
                "name": "爆椒鸡",
                "img": "./static/images/baojiao_ji.png",
                "price": 48.00
            }, {
                "name": "辣子鸡丁",
                "img": "./static/images/lazi_ji.png",
                "price": 39.00
            },
            {
                "name": "麻辣鱼头",
                "img": "./static/images/mala_yu.png",
                "price": 52.00
            }, {
                "name": "酸菜鱼",
                "img": "./static/images/lazi_ji.png",
                "price": 53.00
            },
            {
                "name": "红烧带鱼",
                "img": "./static/images/mala_yu.png",
                "price": 42.00
            }, {
                "name": "水煮肉片",
                "img": "./static/images/dapan_ji.png",
                "price": 35.00
            },
            {
                "name": "毛血旺",
                "img": "./static/images/baojiao_ji.png",
                "price": 30.00
            }, {
                "name": "鱼香肉丝",
                "img": "./static/images/lazi_ji.png",
                "price": 18.00
            },
            {
                "name": "烧大肠",
                "img": "./static/images/mala_yu.png",
                "price": 28.00
            }, {
                "name": "农家小炒肉",
                "img": "./static/images/lazi_ji.png",
                "price": 25.00
            },
            {
                "name": "青椒肉丝",
                "img": "./static/images/mala_yu.png",
                "price": 20.00
            }, {
                "name": "京酱肉丝",
                "img": "./static/images/mala_yu.png",
                "price": 19.00
            }, {
                "name": "红烧肉(带饼)",
                "img": "./static/images/lazi_ji.png",
                "price": 30.00
            },
            {
                "name": "回锅肉",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "木须肉",
                "img": "./static/images/mala_yu.png",
                "price": 18.00
            }, {
                "name": "酸豆角炒肉末",
                "img": "./static/images/lazi_ji.png",
                "price": 22.00
            },
            {
                "name": "鲜辣烤鱼",
                "img": "./static/images/mala_yu.png",
                "price": 79.00
            }, {
                "name": "干锅菜花",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "干锅千页豆腐",
                "img": "./static/images/lazi_ji.png",
                "price": 15.00
            },
            {
                "name": "西红柿烧牛楠",
                "img": "./static/images/mala_yu.png",
                "price": 29.00
            }, {
                "name": "绿豆牙炒馓子",
                "img": "./static/images/mala_yu.png",
                "price": 13.00
            }, {
                "name": "西红柿炒鸡蛋",
                "img": "./static/images/lazi_ji.png",
                "price": 12.00
            },
            {
                "name": "酸辣土豆丝",
                "img": "./static/images/mala_yu.png",
                "price": 14.00
            }, {
                "name": "香菇青菜",
                "img": "./static/images/mala_yu.png",
                "price": 17.00
            }, {
                "name": "麻辣豆腐",
                "img": "./static/images/lazi_ji.png",
                "price": 10.00
            },
            {
                "name": "鱼香茄子",
                "img": "./static/images/mala_yu.png",
                "price": 15.00
            }, {
                "name": "爆炒花哈",
                "img": "./static/images/mala_yu.png",
                "price": 23.00
            }, {
                "name": "豆芽粉条",
                "img": "./static/images/lazi_ji.png",
                "price": 11.00
            },
            {
                "name": "红油耳丝",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "变蛋黄豆",
                "img": "./static/images/mala_yu.png",
                "price": 9.00
            }, {
                "name": "麻辣花生米",
                "img": "./static/images/lazi_ji.png",
                "price": 9.00
            },
            {
                "name": "水蒸蛋",
                "img": "./static/images/mala_yu.png",
                "price": 10.00
            }, {
                "name": "烧茄子",
                "img": "./static/images/mala_yu.png",
                "price": 14.00
            }, {
                "name": "炒饼丝",
                "img": "./static/images/lazi_ji.png",
                "price": 8.00
            }];
        foods = json.dumps(foods_json)
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(foods)

从代码上看,首先定义一个餐饮食品的列表,调用json.jumps()方法把列表转成json数据,因为在餐饮食品列表中有中文字符,setheader()方法实现将显示的内容编码类型设置成“UTF-8”,setheader()中前一个值是设置请求头的哪一个选项,Content-Type就是显示的内容类型选项。“application/json;charset=utf8”指的是显示的是json数据,并且数据的文字编码格式为“UTF-8”。最后在页面上写入foods这个json化的数据。

现在,用http://localhost:8001这个地址去访问,这里返回的就是json数据。如下图所示。

从图片上显示的数据上来看,其显示的是键值对形式的json数据。

五、tornado框架实现并发请求json接口

tornado框架的特性在于性能,也就是并发的特点。如果没有发挥出tornado框架并发的特点,使用起来的tornado框架与django或者flask框架都是一样的。这里就需要处理tornado框架是如何处理并发的。

tornado框架实现并发需要调用户tornado.gen这个模块,在tornado.gen中有coroutine,这是个多协程的技术,这个技术解决并发问题是可以解决c10K的问题的。可以在用户请求类的get方法上加上一个装饰器,这个装饰器的名字就是多协程的技术,也就是在get方法上加入装饰器@tornado.gen.coroutine。这样,get方法中就需要yield来配合去执行对应的方法来实现并发的技术。于是,在被协程修饰的get函数体中加入yield 方法名来实现当并发请求访问数据时,就调用yield后面的方法来实现功能。这样执行的方法调用了多协程的技术,但每次都需要不断产生新的协程,不能对其实现管理,这种协程也叫野协程。而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。为了防止这种现象的出现,引入线程池的概念,更多的引用该技术进行协程的管理。

python可以使用concurrent.futures模块,可以利用ThreadPoolExecutor实现线程池。concurrent.futures会以子进程的形式,平行的运行多个python解释器,从而令python程序可以利用多核CPU来提升执行速度。由于子进程与主解释器相分离,所以他们的全局解释器锁也是相互独立的。每个子进程都能够完整的使用一个CPU内核。

实现这个线程池技术需要实例化用ThreadPoolExecutor,参数表明线程池中的线程大小,比如这里输入10000,那就是这个线程池对10000个协程进行统一的管理,不会无限制的产生协程,导致资源过多而系统瘫痪。

定义结束一个线程池的管理变量executor,然后就可以指定get方法中yield后面的方法在指导的线程池中工作,为yield后面的方法指明装饰器@runonexecutor,这样处理后函数体内的方法逻辑就可以在线程池中运行了。代码如下。

import tornado.httpserver
import tornado.ioloop
import tornado.web
import json
from concurrent.futures import ThreadPoolExecutor
import tornado.concurrent
class MainHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(1000)
    @tornado.gen.coroutine
    def get(self,*args,**kwargs):
        yield self.get_response()
    @tornado.concurrent.run_on_executor
    def get_response(self,*args,**kwargs):
        foods_json = [{
                "name": "野蘑菇炖鸡",
                "img": "./static/images/mogu_ji.png",
                "price": 58.00
            }, {
                "name": "大盘鸡",
                "img": "./static/images/dapan_ji.png",
                "price": 62.00
            },
            {
                "name": "爆椒鸡",
                "img": "./static/images/baojiao_ji.png",
                "price": 48.00
            }, {
                "name": "辣子鸡丁",
                "img": "./static/images/lazi_ji.png",
                "price": 39.00
            },
            {
                "name": "麻辣鱼头",
                "img": "./static/images/mala_yu.png",
                "price": 52.00
            }, {
                "name": "酸菜鱼",
                "img": "./static/images/lazi_ji.png",
                "price": 53.00
            },
            {
                "name": "红烧带鱼",
                "img": "./static/images/mala_yu.png",
                "price": 42.00
            }, {
                "name": "水煮肉片",
                "img": "./static/images/dapan_ji.png",
                "price": 35.00
            },
            {
                "name": "毛血旺",
                "img": "./static/images/baojiao_ji.png",
                "price": 30.00
            }, {
                "name": "鱼香肉丝",
                "img": "./static/images/lazi_ji.png",
                "price": 18.00
            },
            {
                "name": "烧大肠",
                "img": "./static/images/mala_yu.png",
                "price": 28.00
            }, {
                "name": "农家小炒肉",
                "img": "./static/images/lazi_ji.png",
                "price": 25.00
            },
            {
                "name": "青椒肉丝",
                "img": "./static/images/mala_yu.png",
                "price": 20.00
            }, {
                "name": "京酱肉丝",
                "img": "./static/images/mala_yu.png",
                "price": 19.00
            }, {
                "name": "红烧肉(带饼)",
                "img": "./static/images/lazi_ji.png",
                "price": 30.00
            },
            {
                "name": "回锅肉",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "木须肉",
                "img": "./static/images/mala_yu.png",
                "price": 18.00
            }, {
                "name": "酸豆角炒肉末",
                "img": "./static/images/lazi_ji.png",
                "price": 22.00
            },
            {
                "name": "鲜辣烤鱼",
                "img": "./static/images/mala_yu.png",
                "price": 79.00
            }, {
                "name": "干锅菜花",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "干锅千页豆腐",
                "img": "./static/images/lazi_ji.png",
                "price": 15.00
            },
            {
                "name": "西红柿烧牛楠",
                "img": "./static/images/mala_yu.png",
                "price": 29.00
            }, {
                "name": "绿豆牙炒馓子",
                "img": "./static/images/mala_yu.png",
                "price": 13.00
            }, {
                "name": "西红柿炒鸡蛋",
                "img": "./static/images/lazi_ji.png",
                "price": 12.00
            },
            {
                "name": "酸辣土豆丝",
                "img": "./static/images/mala_yu.png",
                "price": 14.00
            }, {
                "name": "香菇青菜",
                "img": "./static/images/mala_yu.png",
                "price": 17.00
            }, {
                "name": "麻辣豆腐",
                "img": "./static/images/lazi_ji.png",
                "price": 10.00
            },
            {
                "name": "鱼香茄子",
                "img": "./static/images/mala_yu.png",
                "price": 15.00
            }, {
                "name": "爆炒花哈",
                "img": "./static/images/mala_yu.png",
                "price": 23.00
            }, {
                "name": "豆芽粉条",
                "img": "./static/images/lazi_ji.png",
                "price": 11.00
            },
            {
                "name": "红油耳丝",
                "img": "./static/images/mala_yu.png",
                "price": 16.00
            }, {
                "name": "变蛋黄豆",
                "img": "./static/images/mala_yu.png",
                "price": 9.00
            }, {
                "name": "麻辣花生米",
                "img": "./static/images/lazi_ji.png",
                "price": 9.00
            },
            {
                "name": "水蒸蛋",
                "img": "./static/images/mala_yu.png",
                "price": 10.00
            }, {
                "name": "烧茄子",
                "img": "./static/images/mala_yu.png",
                "price": 14.00
            }, {
                "name": "炒饼丝",
                "img": "./static/images/lazi_ji.png",
                "price": 8.00
            }];
        foods = json.dumps(foods_json)
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(foods)


if __name__ == '__main__':
    application = tornado.web.Application(
        handlers=[
            (r'/', MainHandler)
        ]
    )
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8001)
    tornado.ioloop.IOLoop.instance().start()

代码运行结果如下图所示。

结果与没有实现并发是一样的,但其实质是在发生并发的时候也会显示这样的json数据结果。

六、Vue使用axios获取tornado接口数据

访问http://localhost:8001这个地址后可以获取到餐饮食品的列表数据后,前端就需要请求获取这个数据,然后在前端页面上展示出来。

前面布局好的前端页面中,foodlist餐饮食品列表是通过data()数据初始化中直接给定的,现在需要使用ajax进行数据的获取,vue一般结合axios技术获取后端传送过来的ajax数据。

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

安装axios可以使用npm软件管理包,安装格式如下。

npm  install axios --save

命令中的参数“--save”表示会把axios插件注册到vue项目目录package.json里面,也就意味着当前的项目可以使用axios插件。

现在就可以使用axios插件发送get请求,获取数据。axios发送get请求获取数据的格式如下。

axios.get("请求地址",{params:参数字典}).then(res=>{   
       console.log(res);
}).catch(err=>{
       console.log(err);
})

堂食点餐系统中现在的foodlist就可以用axios的get请求,把接口的数据获取,赋值给foodlist,也就是说foodlist的数据来源是由axios的get方式去请求tornado的web服务器得来的。这个axios的方法应该写到哪里呢,是不能写入 初始化的,这跟Vue的生命周期有关系。

Vue的生命周期如下图所示。

从生命周期中可以看到一些钩子函数,如:

根据对钩子函数的解释,created是组件实例创建完成后,即el-table被创建后,但dom树还未生成,这时加载数据是比较合适的。因此,将axios的ajax请求放在created钩子函数中,同时要注意在data()初始化数据中把foodlist置空。created钩子函数执行时把foodlist的数据获取,在创建dom树时就形成了el-table的表格数据,也就展示出了所有的餐饮数据列表。代码如下。

<template>
  <div >
      <el-header >
        绝妙订餐系统
      </el-header>
       <el-row>
           <el-col :span="4">
              <el-menu
                background-color="darkgreen"
                text-color="#fff"
               >
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-platform"></i>
                            <span>厨师推荐</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                            <i class="el-icon-s-tools"></i>
                            <span>精品凉菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-user-solid"></i>
                              <span>特色小炒</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>家常热菜</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>营养主食</span>
                        </template>
                  </el-menu-item>
                  <el-menu-item>
                        <template>
                              <i class="el-icon-message-solid"></i>
                              <span>调味饮料</span>
                        </template>
                    </el-menu-item>
              </el-menu>
          </el-col>
         <el-col :span="20">
              <el-table :data="foodlist" ref="myTable" height="330">
                  <el-table-column label="菜名"  prop="name"></el-table-column>
                  <el-table-column label="菜品图片"  prop="img">
                        <template slot-scope="scope">
                          <img :src="scope.row.img" width="170" height="124"/>
                        </template>
                  </el-table-column>
                  <el-table-column label="菜品价格"  prop="price"></el-table-column>
                  <el-table-column label="操作" >
                      <template slot-scope="scope" >
                          <i class="el-icon-circle-plus" @click="plus_goods({scope})"></i>
                      </template>
                  </el-table-column>
              </el-table>
         </el-col>

      </el-row>
      <el-row>
            <el-col :span="2">
              <div class="bg-green-dark">
                  <i class="el-icon-cart"></i>
              </div>
            </el-col>
            <el-col :span="18">
              <div class="bg-green-dark">
                商品{{num_food}}件,{{num_price}}元
              </div>
            </el-col>
            <el-col :span="4">
              <div class="bg-red-dark">
                去结算
              </div>
            </el-col>
      </el-row>

  </div>
</template>

<script>
    import "../assets/logo.png"
    import axios from "axios"
    export default {
       name: 'OrderFood',
       data(){
              return {
                    num_food:0,
                    num_price:0,
                    foodlist:[]
              }
       },
      created(){
            axios.get("http://localhost:8000/index").then((res)=>{
                this.foodlist=res.data
            })
      },
      methods:{
          plus_goods(datas){
              this.num_food++;
              console.log(this.foodlist[datas.scope.$index]["price"])
              this.num_price+=this.foodlist[datas.scope.$index]["price"];
              console.log(this.num_price)

          }
      }
    }
</script>


<style scoped>
      .el-header{
        background-color:darkgreen;
        color:white;
        line-height:60px;
        font-size:20px;
      }
      .el-menu-item:hover{
          background:yellow!important;
          color:purple!important;

      }
      .el-menu-item.is-active{
          background:purple!important;
          color:white!important;

      }
      .el-icon-cart{
         width:60px;
         height:60px;
         background: url("../assets/image/cart.png") center no-repeat;
         background-size:100% 100%;
         margin-top:-10px;
      }
      .el-icon-cart:before{
        visibility: hidden;
      }
      .bg-green-dark{
        background-color:black;
        height:60px;
        line-height:60px;
        color:white;
      }
      .bg-red-dark{
        background-color:darkred;
        color:white;
        height:60px;
        line-height:60px;
      }
</style>

代码执行的结果如下图所示。

从图中下面的控制台看出有错误“已拦截跨源请求”。

七、tornado跨域请求的处理

tornado服务器web程序可以通过设置请求头属性Access-Control-Allow-Origin,表明允许访问的源头是哪里,这里可以指定“”,即所有的都可以访问这个接口,或者是单独的IP或域名都可以,其次还要设置请求头属性的Access-Control-Allow-Headers,将其设置成“”,所有的内容都可以允许。就可以从tornado服务器这里处理跨域请求,当然,客户端也可以实现,这里主要介绍tornado服务器的处理。

代码如下。

self.set_header("Access-Control-Allow-Origin","*")
self.set_header("Access-Control-Allow-Headers","*,Content-Type")

最终服务器端的完整代码如下。

import tornado.web
import tornado.ioloop
import tornado.gen
from concurrent.futures import ThreadPoolExecutor
import tornado.concurrent
import json
class IndexHandler(tornado.web.RequestHandler):

    executor=ThreadPoolExecutor(1000)
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):
        yield self.index_response()
    @tornado.concurrent.run_on_executor
    def index_response(self,*args,**kwargs):
        foods_json=[{
                "name":"野蘑菇炖鸡",
                "img":"./static/images/mogu_ji.png",
                "price":58.00
              },{
                "name":"大盘鸡",
                "img":"./static/images/dapan_ji.png",
                "price":62.00
              },
              {
                "name":"爆椒鸡",
                "img":"./static/images/baojiao_ji.png",
                "price":48.00
              },{
                "name":"辣子鸡丁",
                "img":"./static/images/lazi_ji.png",
                "price":39.00
              },
              {
                "name":"麻辣鱼头",
                "img":"./static/images/mala_yu.png",
                "price":52.00
              },{
                "name":"酸菜鱼",
                "img":"./static/images/lazi_ji.png",
                "price":53.00
              },
              {
                "name":"红烧带鱼",
                "img":"./static/images/mala_yu.png",
                "price":42.00
              },{
                "name":"水煮肉片",
                "img":"./static/images/dapan_ji.png",
                "price":35.00
              },
              {
                "name":"毛血旺",
                "img":"./static/images/baojiao_ji.png",
                "price":30.00
              },{
                "name":"鱼香肉丝",
                "img":"./static/images/lazi_ji.png",
                "price":18.00
              },
              {
                "name":"烧大肠",
                "img":"./static/images/mala_yu.png",
                "price":28.00
              },{
                "name":"农家小炒肉",
                "img":"./static/images/lazi_ji.png",
                "price":25.00
              },
              {
                "name":"青椒肉丝",
                "img":"./static/images/mala_yu.png",
                "price":20.00
              },{
                "name":"京酱肉丝",
                "img":"./static/images/mala_yu.png",
                "price":19.00
              },{
                "name":"红烧肉(带饼)",
                "img":"./static/images/lazi_ji.png",
                "price":30.00
              },
              {
                "name":"回锅肉",
                "img":"./static/images/mala_yu.png",
                "price":16.00
              },{
                "name":"木须肉",
                "img":"./static/images/mala_yu.png",
                "price":18.00
              },{
                "name":"酸豆角炒肉末",
                "img":"./static/images/lazi_ji.png",
                "price":22.00
              },
              {
                "name":"鲜辣烤鱼",
                "img":"./static/images/mala_yu.png",
                "price":79.00
              },{
                "name":"干锅菜花",
                "img":"./static/images/mala_yu.png",
                "price":16.00
              },{
                "name":"干锅千页豆腐",
                "img":"./static/images/lazi_ji.png",
                "price":15.00
              },
              {
                "name":"西红柿烧牛楠",
                "img":"./static/images/mala_yu.png",
                "price":29.00
              },{
                "name":"绿豆牙炒馓子",
                "img":"./static/images/mala_yu.png",
                "price":13.00
              },{
                "name":"西红柿炒鸡蛋",
                "img":"./static/images/lazi_ji.png",
                "price":12.00
              },
              {
                "name":"酸辣土豆丝",
                "img":"./static/images/mala_yu.png",
                "price":14.00
              },{
                "name":"香菇青菜",
                "img":"./static/images/mala_yu.png",
                "price":17.00
              },{
                "name":"麻辣豆腐",
                "img":"./static/images/lazi_ji.png",
                "price":10.00
              },
              {
                "name":"鱼香茄子",
                "img":"./static/images/mala_yu.png",
                "price":15.00
              },{
                "name":"爆炒花哈",
                "img":"./static/images/mala_yu.png",
                "price":23.00
              },{
                "name":"豆芽粉条",
                "img":"./static/images/lazi_ji.png",
                "price":11.00
              },
              {
                "name":"红油耳丝",
                "img":"./static/images/mala_yu.png",
                "price":16.00
              },{
                "name":"变蛋黄豆",
                "img":"./static/images/mala_yu.png",
                "price":9.00
              },{
                "name":"麻辣花生米",
                "img":"./static/images/lazi_ji.png",
                "price":9.00
              },
              {
                "name":"水蒸蛋",
                "img":"./static/images/mala_yu.png",
                "price":10.00
              },{
                "name":"烧茄子",
                "img":"./static/images/mala_yu.png",
                "price":14.00
              },{
                "name":"炒饼丝",
                "img":"./static/images/lazi_ji.png",
                "price":8.00
              }];
        foods=json.dumps(foods_json)
        self.set_header("Access-Control-Allow-Origin","*")
        self.set_header("Access-Control-Allow-Headers","*,Content-Type")
        self.set_header("Content-Type","application/json; charset=UTF-8")
        self.write(foods)

启动服务器端命令:

Python   tst_web.py

这里tst_web.py是服务器端python程序代码文件名

启动客户端程序需要在客户端程序目录下执行。

npm run dev 

通过浏览器访问客户端程序,地址是http://localhost:8080,执行结果如下图所示。

代码github地址:https://github.com/wawacode/tornado-vue-order_food_system

项目对应的视频对址:

vue堂食系统2-vue技术实现组件间的通信
https://www.bilibili.com/video/BV1ef4y1z7FS/
vue堂食系统3-tornado并发的实现
https://www.bilibili.com/video/BV13N411d7Gp/

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python高效开发实战:Django、Tornado、Flask和Twisted是一本有关Python网络开发框架的教程,以PDF格式发布。这本书主要介绍了这四个框架的特点、用法和实际应用场景。 首先,Django是一个全功能的Web应用框架,它提供了大量的开箱即用的功能和工具,包括ORM(对象关系映射),模板引擎和路由系统等。Django适合构建大型、复杂的Web应用程序,它的优势在于快速开发和强大的安全性。 其次,Tornado是一个异步的Web框架,它具有高性能和可伸缩性。Tornado适用于处理大量并发请求的场景,它采用非阻塞IO模型和事件驱动的设计,可以轻松地处理高负载的网络应用。 另外,Flask是一个简洁灵活的微框架,它注重简单性和可扩展性。Flask非常适合构建小型的Web应用程序或者API接口,它是一个基础而易于上手的框架,同时也提供了大量的扩展库供开发者使用。 最后,Twisted是一个异步网络编程框架,它可以构建各种类型的网络应用程序,包括Web服务器、邮件服务器和聊天程序等。Twisted提供了强大的异步网络编程支持,可以轻松地处理并发连接和高负载情况。 在这本书,读者将学习到如何使用这四个框架来构建实际的网络应用程序。通过实战案例和示例代码,读者可以深入了解每个框架的特点和用法。同时,书还介绍了一些常见的最佳实践和开发技巧,帮助读者提高开发效率和代码质量。 总而言之,这本书将帮助读者全面了解Python网络开发框架,并通过实际项目的案例帮助读者掌握框架的使用技巧和开发经验。无论是对于初学者还是有一定经验的开发者来说,这本书都是一本不可错过的学习资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值