Python实现搭建-简单服务器教程
Posted Python学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python实现搭建-简单服务器教程相关的知识,希望对你有一定的参考价值。
Python动态服务器网页(需要使用WSGI接口),基本实现步骤如下:
1.等待客户端的链接,服务器会收到一个http协议的请求数据报
2.利用正则表达式对这个请求数据报进行解析(请求方式、提取出文件的环境)
3.提取出文件的环境之后,利用截断取片的方法将文件名转化为模块名称
4.使用m = __import__()
,就可以得到返回值为m的模块
5.创建一个env字典:其中包含的是请求方式及文件环境等各种的键值对
6.创建一个新的动态脚本,其中定义了application这个函数,必须包含env和start_response的参数(也是服务器里的调用方法)
7.在这个动态脚本中定义状态码status和响应头headers(注意是字典形式,如Content-Type)
8.然后再调用start_response(status,headers),但是要注意,这个函数在服务器被定义
9.在动态脚本中编写动态执行程序
10.m.appliction的返回值就是回应数据包的body,它的数据头在start_response被整合
11.将数据头与数据body拼接起来,然后发送给客户端,就可显示动态网页
MyWebServer
import socket
import re
import sys
from multiprocessing import Process
from MyWebFramework import Application
# 设置静态文件根目录
HTML_ROOT_DIR = "./html"
WSGI_PYTHON_DIR = "./wsgipython"
class HTTPServer(object):
""""""
def __init__(self, application):
"""构造函数, application指的是框架的app"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.app = application
def start(self):
self.server_socket.listen(128)
while True:
client_socket, client_address = self.server_socket.accept()
#print("[%s,%s]用户连接上了" % (client_address[0],client_address[1]))
print("[%s, %s]用户连接上了" % client_address)
handle_client_process = Process(target=self.handle_client, args=(client_socket,))
handle_client_process.start()
client_socket.close()
def start_response(self, status, headers):
"""
status = "200 OK"
headers = [
("Content-Type", "text/plain")
]
star
"""
response_headers = "HTTP/1.1 " + status + "\\r\\n"
for header in headers:
response_headers += "%s: %s\\r\\n" % header
self.response_headers = response_headers
def handle_client(self, client_socket):
"""处理客户端请求"""
# 获取客户端请求数据
request_data = client_socket.recv(1024)
print("request data:", request_data)
request_lines = request_data.splitlines()
for line in request_lines:
print(line)
# 解析请求报文
# \'GET / HTTP/1.1\'
request_start_line = request_lines[0]
# 提取用户请求的文件名
print("*" * 10)
print(request_start_line.decode("utf-8"))
file_name = re.match(r"\\w+ +(/[^ ]*) ", request_start_line.decode("utf-8")).group(1)
method = re.match(r"(\\w+) +/[^ ]* ", request_start_line.decode("utf-8")).group(1)
env =
"PATH_INFO": file_name,
"METHOD": method
response_body = self.app(env, self.start_response)
response = self.response_headers + "\\r\\n" + response_body
# 向客户端返回响应数据
client_socket.send(bytes(response, "utf-8"))
# 关闭客户端连接
client_socket.close()
def bind(self, port):
self.server_socket.bind(("", port))
def main():
sys.path.insert(1, WSGI_PYTHON_DIR)
if len(sys.argv) < 2:
sys.exit("python MyWebServer.py Module:app")
# python MyWebServer.py MyWebFrameWork:app
module_name, app_name = sys.argv[1].split(":")
# module_name = "MyWebFrameWork"
# app_name = "app"
m = __import__(module_name)
app = getattr(m, app_name)
http_server = HTTPServer(app)
# http_server.set_port
http_server.bind(8000)
http_server.start()
if __name__ == "__main__":
main()
MyWebFrameWork
import time
# from MyWebServer import HTTPServer
# 设置静态文件根目录
HTML_ROOT_DIR = "./html"
class Application(object):
"""框架的核心部分,也就是框架的主题程序,框架是通用的"""
def __init__(self, urls):
# 设置路由信息
self.urls = urls
def __call__(self, env, start_response):
path = env.get("PATH_INFO", "/")
# /static/index.html
if path.startswith("/static"):
# 要访问静态文件
file_name = path[7:]
# 打开文件,读取内容
try:
file = open(HTML_ROOT_DIR + file_name, "rb")
except IOError:
# 代表未找到路由信息,404错误
status = "404 Not Found"
headers = []
start_response(status, headers)
return "not found"
else:
file_data = file.read()
file.close()
status = "200 OK"
headers = []
start_response(status, headers)
return file_data.decode("utf-8")
for url, handler in self.urls:
#("/ctime", show_ctime)
if path == url:
return handler(env, start_response)
# 代表未找到路由信息,404错误
status = "404 Not Found"
headers = []
start_response(status, headers)
return "not found"
def show_ctime(env, start_response):
status = "200 OK"
headers = [
("Content-Type", "text/plain")
]
start_response(status, headers)
return time.ctime()
def say_hello(env, start_response):
status = "200 OK"
headers = [
("Content-Type", "text/plain")
]
start_response(status, headers)
return "hello itcast"
def say_haha(env, start_response):
status = "200 OK"
headers = [
("Content-Type", "text/plain")
]
start_response(status, headers)
return "hello haha"
urls = [
("/", show_ctime),
("/ctime", show_ctime),
("/sayhello", say_hello),
("/sayhaha", say_haha),
]
app = Application(urls)
# if __name__ == "__main__":
# urls = [
# ("/", show_ctime),
# ("/ctime", show_ctime),
# ("/sayhello", say_hello),
# ("/sayhaha", say_haha),
# ]
# app = Application(urls)
# http_server = HTTPServer(app)
# http_server.bind(8000)
# http_server.start()
CentOS7下安装RabbitMQ,并使用Spring Boot实现一个简单的延迟队列(小白教程,附源码)
文章目录
笔者在安装RabbitMQ时发现大部分博客记录的安装复杂且版本固定且过时,或复制粘贴缺胳膊少腿;故写下此博客,希望有需要的同学可以少走些弯路。
笔者设备: win10+Linux CentOS7。
笔者RabbitMQ版本3.8.16,Java版本11,Spring Boot版本2.4.5。
使用win10系统去连接服务器不需要安装其它任何软件。
服务器安装使用root用户,使用rpm包。
本文宗旨操作简单化,描述详细化 。
项目源码:https://github.com/MuweiLaw/RabbitMQ.git
项目源码分为两个服务,一个生产者一个消费者,适用于分布式架构。
一.环境搭建
1-安装包和插件准备
1.1在本地新建空文件夹存放rpm安装包
在PC上新建一个文件夹用来存放安装包
例如我在E盘上新建一个空文件夹,路径为E:\\upload
1.2下载RabbitMQ
进入 RabbitMQ下载地址,下载RabbitMQ的包。
笔者的Linux版本均为CentOS7,故下载图中蓝框版本
蓝色框上方为CentOS8版本
点击下载放入到我们刚才建好的文件夹中
1.3下载RabbitMQ的延迟插件
进入延迟插件下载地址,在页面使用Ctrl+F直接搜索rabbitmq_delayed_message_exchange即可找到我们需要下载的插件
下载和RabbitMQ配套的版本
1.4下载ErLang
RabbitMQ是基于Erlang语言开发,so在安装RabbitMQ之前,需要先安装Erlang,这里使用RabbitM
Q团队提供的Erlang依赖包。
进入Erlang下载页
点击下载放入到我们刚才建好的文件夹中
2-上传安装包和插件到服务器
2.1上传准备
此时我们已经下载好了3个包并放在了E:\\upload文件夹下
2.2上传本地文件到服务器
回到电脑桌面
使用快捷键win+R
在弹出的窗口输入cmd
回车后打开windows运行命令行窗口,笔者的字体颜色是绿色(人生在世总要带点绿哈哈哈),大部分同学的字体颜色是白色
输入命令sftp user@host建立sftp连接,user是你的Linux账户名,host是你的Linux系统对外的IP地址。注意,密码不会显示,带有小键盘的同学尽量不要用小键盘。输入密码后回车显示
Connected to user@host.
则表示连接成功。如下:
连接成功后输入命令lcd E:\\upload
再输入命令lls
就能看到我们之前放在 E:\\upload 里的三个文件
好了,现在我们开始本地上传这三个文件,put 加上对应的文件名称
put erlang-23.3.2-1.el7.x86_64.rpm
put rabbitmq_delayed_message_exchange-3.8.0.ez
put rabbitmq-server-3.8.16-1.el7.noarch.rpm
再接着,我们把这三个文件保存到服务器,get 加上对应的文件名称
get erlang-23.3.2-1.el7.x86_64.rpm
get rabbitmq_delayed_message_exchange-3.8.0.ez
get rabbitmq-server-3.8.16-1.el7.noarch.rpm
我们使用命令ls查看服务器是否保存这三个文件
使用命令pwd知道服务器当前保存的文件夹为 /root
如下:
最后,我们使用Ctrl+C关闭sftp连接
2.3注意事项
IP查看方式:
1.虚拟机: 使用ifconfig命令
2.云服务器:在云服务器控制台上找到服务器的公网IP,笔者是一台最低配阿里云ECS服务器,年费用不超过100软妹币,满足日常学习开发使用,其它高配云服务器享受同等折扣,链接戳我直达
第一次使用sftp传输文件回有如下提示
3-安装
3.1使用win10自带ssh连接服务器
使用Xshell,PuTTY,CRT等ssh工具的可以跳过这一步
打开windows运行命令行
输入ssh user@host这里user依旧是你的Linux账户名,host依旧是你的Linux系统对外的IP地址。密码不显示,输入密码时尽量不要使用小键盘。
图示我们已经通过ssh用root账号连接上了我们的服务器,接下来我们将正式在服务器搭建环境
3.2正式搭建
登录到服务器成功之后
使用 ls 即可查看我们之前通过sftp传输的三个文件。
依次执行如下命令:
rpm -ivh erlang-23.3.2-1.el7.x86_64.rpm 安装erlang依赖
yum -y install socat 安装socat
rpm -ivh rabbitmq-server-3.8.16-1.el7.noarch.rpm 安装RabbitMQ
接下来我们启动RabbitMQ
使用命令 systemctl start rabbitmq-server 稍等几秒钟,正常启动如下,没有任何提示
systemctl enable rabbitmq-server 设置RabbitMQ开机自启
rabbitmq-plugins enable rabbitmq_management 启用web控制台
开放端口(防火墙已关闭的同学可以跳过这一小步)
firewall-cmd --zone=public --add-port=5672/tcp --permanent 开放客户端端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent 开放web控制台端口
firewall-cmd --reload 重启防火墙
需要注意一点的是,使用阿里云服务器的同学还需要配置安全组
尝试登录web控制台
RabbitMQ默认的账号用户名和密码都是guest。
在浏览器输入 http://host:15672/ 即可访问web控制台,这里的 host 依旧是你的服务器对外IP。
例如笔者的web控制台的Url为 http://10.1.2.4:15672/
默认情况下,RabbitMQ的默认的guest用户只允许本机访问,当我们在web控制台输入账号密码后会发现如下图所示
所以我们还需要让guest用户能够远程访问
cd /etc/rabbitmq 进入到 etc/rabbitmq 目录下
touch rabbitmq.config 创建名为 rabbitmq.config 的配置文件
vi rabbitmq.config 编辑配置文件
按下回车键之后效果如下:注意红框内描述变化
此时我们按下键盘 “ i ” 键,进行编辑,注意左下角变化,表示已进入编辑状态
输入 [{rabbit, [{loopback_users, []}]}].
再按下键盘“ Esc ”键退出编辑模式键盘依次按下 :wq
再按下回车键后保存并退出,注意此处冒号是英文冒号
systemctl restart rabbitmq-server重启RabbitMQ,等待数秒
再次访问web控制台
重新输入guest账号密码即可实现登录
安装延迟插件
cd /usr/lib/rabbitmq/lib 先进入lib目录,
再使用 ls 查看RabbitMQ安装目录名称,因为版本因素,各位同学此处的目录名称可能也存在不同
好了,知道黄色框内该版本的安装目录名称之后
cd rabbitmq_server-3.8.16/plugins 我们直接进入插件库文件夹内
ls 查看,后缀 .ez 的文件都是RabbitMQ自带的插件
cp /root/rabbitmq_delayed_message_exchange-3.8.0.ez ./ 我们将先前保存在 /root 目录下的延迟插件 rabbitmq_delayed_message_exchange-3.8.0.ez 拷贝到当前目录。
ls 可以查看到,我们的延迟插件已经拷贝到当前文件夹了
现在我们要启用插件了
cd …/sbin 进入sbin目录(注意注意注意!!CSDN的bug,导致此处命令高亮区域显示了三个“ . ”,下图是两个“ . ”,直接复制粘贴时注意)
rabbitmq-plugins enable rabbitmq_delayed_message_exchange 启用插件成功后就可以看到如下信息
再进入到我们的web控制台,哎哟新增了一个交换机类型可选项 x-delayed-message
环境搭建大功告成!!!
二.代码实现一个简单的订单超时自动取消功能
项目源码:https://github.com/MuweiLaw/RabbitMQ.git
1-代码编辑
笔者用的IDE是intellij IDEA,新建两个Spring Initializr项目
项目1:mq_producer
项目2:mq_consumer两个项目的结构类似,如下:
首先我们需要在两个项目中的pom.xml文件中都添加这3个依赖;
<!--消息队列相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- JSON支持,消息序列化用到 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
之后在application.properties添加RabbitMQ的相关配置;
mq_producer:
server.port=52320
spring.application.name=Spring-boot-rabbitmq
spring.rabbitmq.host=10.1.2.4
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#开启confirm 确认机制,如果对异步消息需要回调必须设置为correlated
#spring.rabbitmq.publisher-confirms=true 已过时,下行替代
spring.rabbitmq.publisher-confirm-type=correlated
mq_consumer:
server.port=52321
spring.rabbitmq.host=10.1.2.4
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.publisher-confirm-type=correlated
接下来创建RabbitMQ的Java配置,主要用于配置交换机、队列和绑定关系,两个项目的配置相同,如下。
枚举类
/**
* 消息队列枚举配置
*
* @author Murray
* @date 2021/5/6 14:30
*/
public enum QueueEnum {
/**
* 插件实现延迟队列
*/
QUEUE_ORDER_PLUGIN_CANCEL("mall.order.direct.plugin", "mall.order.cancel.plugin", "mall.order.cancel.plugin");
/**
* 交换名称
*/
private String exchange;
/**
* 队列名称
*/
private String name;
/**
* 路由键
*/
private String routeKey;
QueueEnum(String exchange, String name, String routeKey) {
this.exchange = exchange;
this.name = name;
this.routeKey = routeKey;
}
public String getExchange() {
return exchange;
}
public String getName() {
return name;
}
public String getRouteKey() {
return routeKey;
}
}
RabbitMQ配置类
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* RabbitMQ配置类
*
* @author Murray
* @date 2021/5/6 14:24
*/
@Configuration
public class RabbitConfig {
/**
* 订单延迟插件消息队列所绑定的交换机
*/
@Bean
CustomExchange orderPluginDirect() {
//创建一个自定义交换机,可以发送延迟消息
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), "x-delayed-message", true, false, args);
}
/**
* 订单延迟插件队列
*/
@Bean
public Queue orderPluginQueue() {
return new Queue(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getName());
}
/**
* 将订单延迟插件队列绑定到交换机
*/
@Bean
public Binding orderPluginBinding(CustomExchange orderPluginDirect, Queue orderPluginQueue) {
return BindingBuilder
.bind(orderPluginQueue)
.to(orderPluginDirect)
.with(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey())
.noargs();
}
/**
* 将默认的消息转换器替换成json消息转换器
*/
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
两个项目都需要订单实体类,且包名也得一样
import java.io.Serializable;
import java.util.UUID;
/**
* 订单实体类
*
* @author Murray Law
* @date 2021/5/8 14:30
*/
public class Order implements Serializable {
private static final long serialVersionUID = -8432910728496351007L;
String orderNumber;
String userName;
Integer age;
String remark;
public Order() {
}
public Order(String userName, Integer age, String remark) {
this.orderNumber = UUID.randomUUID().toString().trim().replaceAll("-", "");
this.userName = userName;
this.age = age;
this.remark = remark;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "Order{" +
"orderNumber='" + orderNumber + '\\'' +
", userName='" + userName + '\\'' +
", age=" + age +
", remark='" + remark + '\\'' +
'}';
}
}
mq_producer 里创建一个 延时消息队列发出者,通过给消息设置x-delay头来设置消息从交换机发送到队列的延迟时间
import com.murray.mq.producer.config.QueueEnum;
import com.murray.mq.commons.entity.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 订单自动取消的消息发出者
*
* @author Murray Law
* @date 2021/5/6 13:24
*/
@Component
public class CancelOrderSender {
private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderSender.class);
@Autowired
private AmqpTemplate amqpTemplate;
public void sendMessage(Order order, final long delayTimes) {
//给延迟队列发送消息
amqpTemplate.convertAndSend(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey(), order, message -> {
//给消息设置延迟毫秒值
message.getMessageProperties().setHeader("x-delay", delayTimes);
return message;
});
LOGGER.info("发送出了一个延时取消订单{}", order);
}
}
mq_producer 里还需要一个订单服务,在订单生成之后调用延时消息发出者的方法
import com.murray.mq.commons.entity.Order;
import com.murray.mq.producer.sender.CancelOrderSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 前台订单管理Service
*
* @author Murray Law
* @date 2021/5/6 13:26
*/
@Service
public class OrderService {
@Autowired
private CancelOrderSender cancelOrderSender;
public void generateOrder(Order order) {
//TODO 实际项目中会执行一系类下单操作
//下单完成后开启一个延迟消息,用于当用户没有付款时取消订单
sendDelayMessageCancelOrder(order);
}
private void sendDelayMessageCancelOrder(Order order) {
//获取订单超时时间,假设为60分钟(测试用的10秒)
long delayTimes = 10 * 1000;
//发送延迟消息
cancelOrderSender.sendMessage(order, delayTimes);
}
}
既然我们有了消息发出者和订单发出服务,那我们就需要在 mq_consumer 里创建一个延时消息接收者用于处理订单延迟队列中的消息,还有收到订单被取消消息后的订单取消服务。
import com.murray.mq.commons.entity.Order;
import com.murray.mq.consumer.service.CancelOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 订单自动取消的消息接受者
*
* @author Murray Law
* @date 2021/5/6 13:23
*/
@Component
@RabbitListener(queues = "mall.order.cancel.plugin")
public class CancelOrderReceiver {
private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderReceiver.class);
@Autowired
private CancelOrderService portalOrderService;
@RabbitHandler
public void handle(Order order) {
LOGGER.info("接受到一个订单因超时未支付{}", order);
portalOrderService.cancelOrder(order);
}
}
import com.murray.mq.commons.entity.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* 前台订单管理Service
*
* @author Murray Law
* @date 2021/5/6 13:26
*/
@Service
public class CancelOrderService {
private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderService.class);
public void cancelOrder(Order order) {
//TODO 实际项目中执行一系类取消订单操作
LOGGER.info("已取消订单{}", order);
}
}
好了,我们启动消息消费端项目 mq_consumer,清空控制台上的启动log,方便我们直观看出新的log记录
清空后Appium+Python3环境搭建,其实超简单!软件测试教程
搭建python简单环境与集成开发环境PyCharm安装教程
最全Python+Selenium环境搭建教程-你绝对想不到有这么简单!