aws python库
I will be using the async python web framework AIOHTTP. In case that you may not have heard or used it before I would advice you to jump quickly to the official documentation here and briefly skim through the introduction.
我将使用异步python网络框架AIOHTTP。 如果您可能没有听说过或使用过它,我建议您快速跳转到此处的官方文档,并简要浏览一下简介。
All the code used in this tutorial is also on github.
本教程中使用的所有代码也在github上 。
HTTP (HTTP)
Lets start off with the HTTP server! I am going to use [email protected] and pipenv as my package manager and virtual environment but feel free to use any other tool that you might be familiar with. Run the following to start a new python3 environment:
让我们从HTTP服务器开始! 我将使用[email protected]和pipenv作为我的包管理器和虚拟环境,但可以随意使用您可能熟悉的任何其他工具。 运行以下命令以启动新的python3环境:
pipenv --python 3.7
Before writing any code, we need to first install our dependencies. For the time being, aiohttp
will suffice to get the HTTP server up and running. so lets install it:
在编写任何代码之前,我们需要先安装依赖项。 暂时, aiohttp
将足以启动并运行HTTP服务器。 所以让我们安装它:
pipenv install aiohttp
Now we can start off with our Application
class.
现在,我们可以从Application
类开始。
# application.pyfrom aiohttp import web
class Application(web.Application):
def __init__(self):
super().__init__() def run(self):
return web.run_app(self, port=8000)
application = Application()if __name__ == '__main__':
application.run()
On a quick note, you have to call the new application instance ‘application’ since elastic beanstalk expects that variable name by default when booting the app, direct quote of the docstring from on the official doc:
简要说明一下 ,您必须将新的应用程序实例称为“应用程序”,因为弹性beantalk在启动应用程序时默认情况下会使用该变量名,直接在官方文档上引用文档字符串:
“EB looks for an ‘application’ callable by default.”
“ EB寻找默认情况下可调用的“应用程序”。 ”
At this point you have a fully runnable web server:
至此,您已经可以完全运行Web服务器:
python application.py>>> ======== Running on http://0.0.0.0:8000 ========
Nothing really exciting since we don’t have any routes, so lets make a ‘/helloworld’ page.
因为我们没有任何路线,所以没有什么令人兴奋的,所以让我们制作一个'/ helloworld'页面。
# application.pyfrom aiohttp import webclass HelloWorldView(web.View):
async def get(self) -> web.Response:
return web.Response(text="Hello World!")
class Application(web.Application):
def __init__(self):
super().__init__()self.add_routes() def add_routes(self):
self.router.add_view('/helloworld', HelloWorldView) def run(self):
web.run_app(self, port=8000)
application = Application()if __name__ == '__main__':
application.run()
Restart the server and enter the following url: ‘http://localhost:8000/helloworld’ in your browser. Voila, you should now see a “Hello World!” text on the page, sweet!
重新启动服务器,然后在浏览器中输入以下URL:' http:// localhost:8000 / helloworld '。 瞧,您现在应该看到一个“ Hello World! 页面上的文字,甜蜜!
GRPC (GRPC)
Now that we have a working HTTP server, lets add the GRPC server. For that, we will first need to define our service.proto schema. You can read more about protobuf here.
现在我们有了一个正常工作的HTTP服务器,让我们添加GRPC服务器。 为此,我们首先需要定义我们的service.proto模式。 您可以在此处阅读有关protobuf的更多信息。
// service.protosyntax = "proto3";
package service;service HelloWorldService {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}message HelloRequest {
string name = 1;
}message HelloResponse {
string message = 1;
}
Here we defined an RPC service that exposes a method called Hello
that takes as argument a request with a name
attribute and returns a response with a message
attribute. We then need to compile the proto file to a more usable python module by running the following:
在这里,我们定义了一个RPC服务,该服务公开了一个名为Hello
的方法,该方法将带有name
属性的请求作为参数并返回带有message
属性的响应。 然后,我们需要通过运行以下命令将proto文件编译为更可用的python模块:
python -m grpc_tools.protoc -I .. --python_out=/ --grpc_python_out=/ service.proto>>> Error while finding module specification for 'grpc_tools.protoc' (ModuleNotFoundError: No module named 'grpc_tools'
Oops! we forgot to install the packages to compile the protobuf schema…
糟糕! 我们忘记安装软件包来编译protobuf模式…
pipenv install grpcio
pipenv install grpcio-tools
Upon running the command again, you will see 2 auto-generated python files ‘service_pb2.py’ and ‘service_pb2_grpc.py’. Now we have everything needed for our GRPC server, we can add a new GrpcServer
class and a HelloServicer
class to handle incoming RPC requests.
再次运行命令后,您将看到2个自动生成的python文件“ service_pb2.py”和“ service_pb2_grpc.py”。 现在我们有了GRPC服务器所需的一切,我们可以添加一个新的GrpcServer
类和一个HelloServicer
类来处理传入的RPC请求。
# application.pyimport asyncio
import grpcfrom aiohttp import webfrom grpc.experimental.aio import init_grpc_aio
from service_pb2_grpc import add_HelloWorldServiceServicer_to_server
from service_pb2_grpc import HelloWorldServiceServicer
class HelloWorldView(web.View):
async def get(self):
return web.Response(text="Hello World!")
class Application(web.Application):
def __init__(self):
super().__init__() self.grpc_task = None
self.grpc_server = GrpcServer() self.add_routes() def add_routes(self):
self.router.add_view('/helloworld', HelloWorldView) def run(self):
return web.run_app(self, port=8000)class HelloServicer(HelloWorldServiceServicer):
def Hello(self, request, context):
response = HelloResponse()
response.message = "Hello {}!".format(request.name)
return responseclass GrpcServer:
def __init__(self):
init_grpc_aio() self.server = grpc.experimental.aio.server()
self.servicer = HelloServicer() add_HelloWorldServiceServicer_to_server(
self.servicer,
self.server) self.server.add_insecure_port("[::]:50051") async def start(self):
await self.server.start()
await self.server.wait_for_termination() async def stop(self):
await self.servicer.stop(0)
application = Application()if __name__ == '__main__':
application.run()
In the HelloServicer
we implemented the Hello
method to match the RPC definition in the service.proto file. Also, note that we are running the GRPC server on port 50051
to allow the client to connect to the 2 servers individually.
在HelloServicer
我们实现了Hello
方法以匹配service.proto文件中的RPC定义。 另外,请注意,我们正在端口50051
上运行GRPC服务器,以允许客户端分别连接到2个服务器。
Piecing things together…
拼凑在一起…
# application.pyimport asyncio
import grpcfrom aiohttp import web
from grpc.experimental.aio import init_grpc_aio
from service_pb2 import HelloResponse
from service_pb2_grpc import add_HelloWorldServiceServicer_to_server
from service_pb2_grpc import HelloWorldServiceServicer
class HelloWorldView(web.View):
async def get(self):
return web.Response(text="Hello World!")
class Application(web.Application):
def __init__(self):
super().__init__() self.grpc_task = None
self.grpc_server = GrpcServer() self.add_routes() self.on_startup.append(self.__on_startup())
self.on_shutdown.append(self.__on_shutdown()) def __on_startup(self):
async def _on_startup(app):
self.grpc_task = \
asyncio.ensure_future(app.grpc_server.start()) return _on_startup def __on_shutdown(self):
async def _on_shutdown(app):
await app.grpc_server.stop()
app.grpc_task.cancel()
await app.grpc_task return _on_shutdown def add_routes(self):
self.router.add_view('/helloworld', HelloWorldView) def run(self):
return web.run_app(self, port=8000)
class HelloServicer(HelloWorldServiceServicer):
def Hello(self, request, context):
response = HelloResponse()
response.message = "Hello {}!".format(request.name)
return response
class GrpcServer:
def __init__(self):
init_grpc_aio() self.server = grpc.experimental.aio.server()
self.servicer = HelloServicer() add_HelloWorldServiceServicer_to_server(
self.servicer,
self.server) self.server.add_insecure_port("[::]:50051") async def start(self):
await self.server.start()
await self.server.wait_for_termination() async def stop(self):
await self.servicer.close()
await self.server.wait_for_termination()
application = Application()if __name__ == '__main__':
application.run()
AIOHTTP provides a on_startup
and a on_shutdown
hook which triggers some registered method when the Application
starts and shutdown respectively. In our case, we also want to start the GrpcServer
in python Future
which will basically sit and wait in the background until a new RPC request comes in to resume its execution in the main python thread.
AIOHTTP提供了一个on_startup
和on_shutdown
挂钩,当Application
启动和关闭时,它们会触发一些已注册的方法。 在我们的情况下,我们GrpcServer
在python Future
启动GrpcServer
,它将基本上在后台坐下来等待直到新的RPC请求进入,以在主python线程中恢复执行。
We can now write a small script to emulate a GRPC client connecting and invoking the RPC call Hello
!
现在,我们可以编写一个小脚本来模拟GRPC客户端,该客户端连接并调用RPC调用Hello
!
# grpc_test_script.pyimport grpcfrom service_pb2_grpc import HelloWorldServiceStub
from service_pb2 import HelloRequestchannel = grpc.insecure_channel('localhost:50051')
hello_world_stub = HelloWorldServiceStub(channel)while True:
name = input("Enter your name: ")
request = HelloRequest()
request.name = name
response = hello_world_stub.Hello(request) print(response.message)
Now run it and enter some text in your terminal
现在运行它并在终端中输入一些文本
Enter your name: Test01
>>> Hello Test01!
Enter your name: Mr Lazy
>>> Hello Mr Lazy!
Enter your name:
Cool our GRPC service works! now comes the fun part.
酷我们的GRPC服务有效! 有趣的来了。
部署到AWS (Deploying to AWS)
I will assume that you already have an account on AWS and ‘eb-cli’ installed locally. But if you don’t already, you can signup for a free account here and follow the installation instructions here to install the ‘eb-cli’.
我将假设您已经在本地安装了AWS帐户和“ eb-cli”帐户。 但是,如果你没有准备好,你可以注册一个免费帐户这里 ,然后按照安装说明这里安装“EB-CLI”。
But prior to deploying, we still have some work left to do. Firstly we need to generate a dependency file (requirement.txt) to allow any other environment to know what dependencies are needed to run our application.
但是在部署之前,我们还有一些工作要做。 首先,我们需要生成一个依赖文件(requirement.txt),以允许任何其他环境知道运行我们的应用程序需要哪些依赖。
pipenv lock -r > requirements.txt
Now we need to create a Procfile
with the following configuration:
现在,我们需要使用以下配置创建一个Procfile
:
web: gunicorn application:application --bind :8000 --worker-class aiohttp.worker.GunicornWebWorker
The Procfile allows us to define our run command with specific configurations. Let us break down the individual components that make up the Procfile:
Procfile允许我们使用特定配置定义运行命令。 让我们分解组成Procfile的各个组件:
web:
Indicates that we want to run the following commands for web server environment. For example, we would have usedworker:
if we were running in a worker environment instead.web:
表示我们要针对Web服务器环境运行以下命令。 例如,我们将使用worker:
如果我们在工作环境中运行。gunicorn
A popular python HTTP server compatible with most python web frameworks. Simply put, it relays the incoming requests from the outside world (from AWS ELB) to our AIOHTTP server.gunicorn
与大多数python Web框架兼容的流行python HTTP服务器。 简而言之,它将来自外界(来自AWS ELB )的传入请求中继到我们的AIOHTTP服务器。application:application
The first ‘application’ indicates the main entry file, in our case, it would be ‘application.py’ and the second one is the name of the variable that points to a WSGI (or Application).application:application
第一个“应用程序”指示主条目文件,在本例中为“ application.py”,第二个是指向WSGI(或应用程序)的变量的名称。--bind
The bind flag indicates which port gunicorn will bind incoming requests to. We are running our HTTP server on port 8000, the flag value is:8000
.--bind
绑定标志指示将端口gillicorn绑定入站请求。 我们正在端口8000上运行HTTP服务器,标志值为:8000
。--worker-class
The class of worker that will process the request. The default worker class isthreads
, which only works for synchronous request handling frameworks like Flask or Django. AIOHTTP being async framework requires its own worker typeGunicornWebWorker
.--worker-class
将处理请求的工作程序类。 默认的工作程序类是threads
,它仅适用于Flask或Django之类的同步请求处理框架。 作为异步框架的AIOHTTP需要其自己的工作程序类型GunicornWebWorker
。
最后 (Finally)
We get to finally deploy our application on AWS and watch it run live!
我们终于可以在AWS上部署我们的应用程序并观看它的实时运行!
- Initialize a new elastic beanstalk application: (For most part, the default configuration will work just fine for us.) 初始化一个新的Elastic beantalk应用程序:(在大多数情况下,默认配置对我们来说很好用。)
eb init>>> Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-south-1 : Asia Pacific (Mumbai)
7) ap-southeast-1 : Asia Pacific (Singapore)
8) ap-southeast-2 : Asia Pacific (Sydney)
9) ap-northeast-1 : Asia Pacific (Tokyo)
10) ap-northeast-2 : Asia Pacific (Seoul)
11) sa-east-1 : South America (Sao Paulo)
12) cn-north-1 : China (Beijing)
13) cn-northwest-1 : China (Ningxia)
14) us-east-2 : US East (Ohio)
15) ca-central-1 : Canada (Central)
16) eu-west-2 : EU (London)
17) eu-west-3 : EU (Paris)
18) eu-north-1 : EU (Stockholm)
19) eu-south-1 : EU (Milano)
20) ap-east-1 : Asia Pacific (Hong Kong)
21) me-south-1 : Middle East (Bahrain)
22) af-south-1 : Africa (Cape Town)
(default is 3): 3>>> Select an application to use
1) [ Create new Application ]
(default is 1): 1>>> Enter Application Name
helloworld>>> It appears you are using Python. Is this correct?
(Y/n): Y>>> Select a platform branch.
1) Python 3.7 running on 64bit Amazon Linux 2
2) Python 3.6 running on 64bit Amazon Linux
3) Python 3.4 running on 64bit Amazon Linux (Deprecated)
4) Python 2.7 running on 64bit Amazon Linux (Deprecated)
5) Python 2.6 running on 64bit Amazon Linux (Deprecated)
6) Preconfigured Docker - Python 3.4 running on 64bit Debian (Deprecated)
(default is 1): 1>>> Do you wish to continue with CodeCommit? (y/N) (default is n):
n>>> Do you want to set up SSH for your instances?
n
- Create a new environment: 创建一个新环境:
eb create helloword-env
- Open the application: 打开应用程序:
eb open
- and DONE! 并做了!
Don’t forget to add a ‘/helloworld’ at the end of the newly deployed URL, since we did not implement an endpoint for ‘/’.
不要忘记在新部署的URL的末尾添加“ / helloworld”,因为我们没有为“ /”实现终结点。
404: Not Found
You can now modify the ‘grpc_test_script.py’ and update to the deployed URL and test things out.
现在,您可以修改“ grpc_test_script.py”并更新为已部署的URL并进行测试。
# grpc_test_script.py...
channel = grpc.insecure_channel("{}:50051".format(EB_DEPLOYED_URL))
...
结论 (Conclusion)
This is by far not a production-ready system but merely a small setup that I needed for a side project. There are a lot of reasons as to why you might want to have both an HTTP & GRPC server running on the same instances at any time. And my use case was that I needed a GRPC service primarily with the ability to accept multiple long-lived websocket connections. I also wanted to have some degree of flexibility in terms of being able to add some basic web pages for health check purposes.
到目前为止,这还不是一个可用于生产的系统,而仅仅是我为辅助项目所需的一个很小的设置。 有许多原因说明为什么您可能希望同时在同一实例上同时运行HTTP和GRPC服务器。 我的用例是,我需要一个GRPC服务,主要是要能够接受多个长期存在的Websocket连接。 我还希望在能够添加一些基本网页以进行健康检查方面具有一定程度的灵活性。
I hope that this small article finds you well and if you have any suggestions and/or improvements, let do leave a comment as I want to learn about them :)
我希望这篇小文章能对您有所帮助,如果您有任何建议和/或改进,请在我要了解的情况下发表评论:)
翻译自: https://medium.com/swlh/running-an-http-grpc-python-server-concurrently-on-aws-elastic-beanstalk-8524d15030e5
aws python库