博客开发简记(3):从页面到后端结构的部署

既然已经能够访问django,那咱们就能够搞点事情了哦,至少来个helloworld吧。html

(一)helloworld

有两个基本的知识点:python

  1. 在浏览器发起一个请求(get),去到django,django调用urls.py来解析地址或参数,因此你能够改为urls.py,来决定对不一样的参数做出不一样的函数处理。
  2. 若是返回一个HttpResponse给浏览器,那浏览器就能够看到展现,因此HttpResponse能够是一个页面。

基于这两个知识点,咱们就改一下urls.py,在请求主页面时,调用一个函数,而且让这个函数返回helloworld的内容。linux

urls.py的主要内容以下:nginx

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from django.http import HttpResponse

def dosomething(request):
    return HttpResponse('<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>你好</title><body>hello world 喂世界 nihao</body><head></html>')

urlpatterns = [
    #path('admin/', admin.site.urls),
    url(r'^$', dosomething)
]

这里定义一个dosomething的函数,注意这个函数是带一个参数的,由于django会传递request给它。而后dosomething返回了一个HttpResponse对象,里面就是一些简单的文字了。git

./manage.py runserver启动django后,在浏览器请求一下,能够看到这样的效果:
helloworld的展现github

这就是一个简单的helloworld程序,太好改了吧。但这里有个问题,上面那段python代码,里面出现一段html,有什么问题吗?html标识的是一个界面,好比用什么标题、用什么字体、用几号大小,等等,这是界面设计的东西,而把界面设计跟代码逻辑或业务逻辑混在一块儿,除非你能容忍混乱并且不影响开发效率,不然界面跟代码逻辑就要分开。那界面提到哪里去呢,就是模板了,就是一个html文件,这里演示一下。web

先mkdir建立一个templates文件夹,之后就放html文件了,而后建立一个main.html,这样设计这个界面(好简单!):shell

<h1>hello,{{yourname}}</h1>

{{yourname}}表示引用一个变量,变量名叫yourname,注意要用双符号来引用变量。在界面中使用变量,这太寻常了,那变量的定义还有值从哪里来的?就是业务逻辑给的呀,这就是界面与逻辑分离的表现,即界面使用逻辑给的变量值,逻辑给界面提供数据。django

好了,而后,天然是要在代码中定义这个yourname变量了,并且代码中要展现这个html,代码以下:后端

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from django.shortcuts import render

def dosomething(request):
    dict = {}
    dict['yourname'] = '广州小程'
    return render(request, 'main.html', dict)

urlpatterns = [
    #path('admin/', admin.site.urls),
    url(r'^$', dosomething)
]

在接到请求时,仍是调用dosomething函数,而后返回一个render,看名字就知道是要渲染绘制一个界面出来,这个界面是什么呢?就是第二个参数指定的main.html,而后第三个参数就是传递给界面的数据,它是一个键值对集,明显,必定要包括yourname这个变量,并且事先给了变量值。

最后要注意一个路径的问题,就是render怎么找到main.html的问题,这个要在settings.py中进行设置,而后来看一下如今的目录结构:
增长main.html的目录结构

按这个结构,要指定的路径就是:项目目录+/myblog/templates

在settings.py中找到 TEMPLATES 这个dict,而后把DIRS的值改为这样:

'DIRS': [BASE_DIR+'/myblog/templates',],

ok,一切就绪,浏览器请求一下,看到这样的效果:
业务与界面发离的展现效果

好了,界面你也知道怎么写了,不就是写html嘛。可是,我要的博客可没有这么简单,须要一步步来开发吗?

在你作一个事情,特别是一个常见的事情以前,看看别人是怎么作的,或许能大大节省你的时间成本。因而,你会发现,博客这种平常操做,在github上有大量的项目。

(二)博客项目

因而,这里我直接使用这个项目:https://github.com/liangliangyy/DjangoBlog

按这个地址的介绍来部署便可:https://www.lylinux.net/article/2019/8/5/58.html

这里有个知识点,为何有了djangoblog,还要用到nginx跟gunicorn呢?它们是什么关系呢?

djangoblog,简单来讲就是一个web应用,也能够说是一个web框架。做为一个应用,djangoblog固然能够runserver起来而且占用80端口等,而后浏览器就能够访问到他。可是,在实际数据交互的场景中,让web应用把负载均衡、高并发之类的事情也作了,是不合适的,应用只应该作本身的业务。因此,还须要一个重要的角色,这个角色就是web服务器,而nginx就是一个web服务器,浏览器的全部请求先到达nginx,nginx先作一些前置的处理(好比静态页面拦截、负载平衡之类),可是,因为nginx不懂业务啊,因此它仍是要调用到djangoblog(web框架或应用),但这个调用不是直接的调用,而是通过中间件,好比gunicorn。gunicorn或wsgi(网关接口),解决了nginx与django交互的问题,由于web服务器与web框架的通讯,要遵照一种协议,而gunicorn正是实现了这种协议。实际上,gunicorn自己也能作为web服务器(相似于nginx),但因能力有限,通常会把这个角色让位于nginx等。因此,简单来讲,一种常见的后端结构就是:nginx+gunicorn+django。

另外,因为gunicorn支持nginx与django的通讯,不可或缺,若是它退出了,有必要即刻启动起来,因而引入supervisor。supervisor监控gunicorn,保证后者的拉起,并且后者以子进程挂在supervisor进程内。因而这个结构也能够说是:nginx+supervisor+gunicorn+django。

最终用浏览器访问,一个网站就出来了:
djangoblog的样子

(三)后端知识

补充一些知识点。

(a)supervisor与gunicorn
supervisor是监控并管理gunicorn的,若是你想中止gunicorn,你只须要把对应的supervisor服务给stop掉就能够了,gunicorn进程会自动中止。

用这个命令先看一下supervisor监控了哪些服务:

supervisorctl status

而后就能够中止这个服务:

supervisorctl stop 服务名

这时服务对应的gunicorn会自动中止,用lsof -i:8000再也不看到进程。 对应于stop,还有start、reload。

固然,若是想直接kill掉gunicorn进程,也能够,先找到它的根pid:

pstree -ap|grep gunicorn

再kill掉:

kill -9 pid

可是,若是supervisor是运行状态,kill掉的gunicorn即刻就会被拉起,一个新的pid的gunicorn又会出现。

(b)supervisor的配置
诸多key-value的配置选项,请自行搜索了解,这里简单说两个。

command,就是supervisor要执行的命令,好比执行某个程序或脚本,好比执行一句python语句等等,涉及到的文件,你能够写完整路径,若是写相对路径则要组合directory这个选项。

directory,在执行command以前,先cd到这个目录,看状况使用(也可不用)。

(c)supervisor的日志
若是遇到supervisor有什么异常而你一时想不出缘由,这时看一下它的日志输出(包括你故意print出来的日志--由于supervisor是python程序因此你固然能够print),也许能帮到你,那日志在哪里呢? 默认在/var/log/supervisor目录下面。若是在配置supervisor时指定了标准输出路径,好比stdout_logfile字段,那就多了一个日志。对于分析问题,这两个日志都应该关注。

(d)supervisor的进程关系
这样查看supervisor的进程关系:

service supervisor status

若是已经设置好supervisor的配置,好比指定启动gunicorn,那就能够看到在supervisor进程下面挂上了gunicorn进程(通常两个,一主一从)。

(e)nginx的配置
nginx的全部配置都在:/etc/nginx 目录,而你新增长的配置应该放在 /etc/nginx/sites-enabled 目录下,这个目录已经有一个叫default的默认配置文件,你的新配置能够任意命名,都将替代default配置,但你须要从新启动nginx。

能够这样重启nginx:

service nginx reload/restart 或:
/etc/init.d/nginx reload

若是不放心,也能够先stop(浏览器请求一下)再start:

service nginx stop
service nginx start

注意,nginx明明stop掉了,用ps也看不到进程了,这时浏览器再请求可能仍是看到页面,这多是浏览器端的缓存。

配置中的error_log字段,指定了nginx出错时的记录文件,这个文件能够帮助你分析nginx的错误。默认不设置这个字段的状况下,错误日志文件是/var/log/nginx/error.log。

若是只是想验证nginx是否能够正常使用,能够写一个最简单的配置,好比这样:

server {
        listen 80;
        server_name www.freep2p.cn;
        root /root/python;
    }

而后在/root/python目录建立一个index.html文件,好比文件内容能够这么简单:

<html><p2>hello world</p2></html>

重启nginx后用 www.freep2p.cn 访问它,就能够看到hello world。

对于nginx的运行状况,能够用下面的命令来查看:

systemctl status nginx.service

(f)定位问题的一个关键
层级一多,问题定位就变得复杂。除了考虑去除层级,好比直接手动调用gunicorn不经supervisor等,这个办法有效外,还有一个关键的点,就是分析日志。

有两个日志要上心,一个是nginx的输出日志(在配置中有写,或者使用默认的路径即/var/log/nginx/error.log),另外一个是supervisor的输出日志(如上介绍,有两个),supervisor的日志,包括了supervisor跟gunicorn的表现,还有请求应答的状况。

(g)可能遇到的问题

(1)Internal Server Error
对于nginx,若是提示“Internal Server Error”,是什么缘由呢? 这个提示(对应错误码是500),意思是服务内部出问题了,但至少说明,你访问到nginx了,只是触发了错误。对于这里的结构,nginx是要调用supervisor+gunicorn的,而gunicorn要调用djangoblog框架,是哪一步出了问题呢?

用lsof -i:8000,能够看到gunicorn已经运行起来了,而nginx的配置相对是简单的,不太像会出问题,那gunicorn到djangoblog那一步怎么样?gunicorn的配置是否是有问题呢?

先把supervisor中止掉,按上面介绍的命令便可作到,supervisor给stop掉后,gunicorn也会自动结束(lsof -i:8000看不到进程),若是这时用浏览器请求一下,会看到这样的提示:

502 Bad Gateway

也就是网关接口出问题了,而实际就是gunicorn给中止了。

尝试用最简单的nginx的配置,能正常访问,因此排除是nginx的问题。

先把supervisor给stop掉,也就是不使用supervisor来启用gunicorn,而是直接执行gunicorn_start.sh脚本,再访问nginx,发现,正常了!

因此,从gunicorn到djangoblog没有问题,问题出在supervisor启用gunicorn引入了问题。

从理论来看,按这里使用的supervisor的配置,supervisor解释执行脚本(不必定用bash),再以exec命令启动gunicorn,而exec的意思就是不另起进程,而是使用当前进程,只要gunicorn不退出(本意就是不退出的),那么当前进程声明的变量以及用source切换至python虚拟环境就是生效的,不用怀疑source不生效。

这时,必定要分析日志。 在supervisor的标准输出/var/log/djangoblog.log中,能够看到这样的提示:
内部错误时supervisor的日志提示

也就是找不到HOST变量!

根据上面的提示,这个是database的设置,代码以下图:
supervisor要读取的环境变量

也就是os.environ.get不到环境变量。

手动执行sh脚本,至关在shell中交互执行,实际是bash进程启动了gunicorn,而supervisor是python程序,它解释sh脚本启动了gunicorn,这两种方式是不一样的,至少bash跟supervisor使用的环境变量的配置就不一样,这个下面就来证实。

先来测试一下supervisor当前的环境变量吧。

把supervisor执行的命令设置一下:

command=python3 -c "import os; print(os.environ)"

而后:

tail -10 /var/log/djangoblog.log

你能够看到已经有一些变量了,可是,没有代码里面的那三个变量,由于尚未设置(设置到~/.bashrc中是无效的)。这样设置到supervisor,一样是在supervisor的配置中修改:

environment = DJANGO_MYSQL_USER='root',DJANGO_MYSQL_PASSWORD='xxx',...

也就是增长environment字段,值就是新增的环境变量,以逗号分隔。

再启supervisor,这个问题就获得解决。

(2)403 forbidden
若是error的log提示,“xxx” is forbidden,一个多是在这个目录下面找不到index.html。


总结一下,本文介绍了怎么简单写helloworld界面,怎么使用开源的博客项目,也重点介绍了后端的知识点。

至此,解决了后端结构的部署问题,网站框架也好了,接下来就是页面个性化类的问题了。

相关文章
相关标签/搜索