前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一个微服务架构的简单示例

一个微服务架构的简单示例

作者头像
程序你好
发布于 2018-07-20 08:40:25
发布于 2018-07-20 08:40:25
3.7K00
代码可运行
举报
文章被收录于专栏:程序你好程序你好
运行总次数:0
代码可运行

最近,在学习微服务架构,看了很多相关的资料,可一直都没有真正动手操作。所以今天,我创建了一个简单的web应用程序示例,让我们通过这个例子来更好地感受微服务的系统架构魅力。这款应用程序做的非常简单:提供一批网上招聘的URL,我们的Web应用就能找到工作描述的文字,并生成一个Word Cloud(词云:许多特定意义的词)。在某些特定的职位招聘中,能够掌握专业技能或流行词汇对HR的人员来说是非常有用的。

微服务应该是独立的、无状态的应用程序,每个应用程序都只关注于某件小事。在这个示例的应用程序中,有以下几个任务:

1)从url指定的页面中检索内容;

2)从工作描述中提取所有词语;

3)创建一个word cloud。

建立这么简单的微服务花费不了多少时间,在下面会详细描述。在实际应用中,我们不可能在网上直接公开发布这些服务,因为没有身份验证、无法防止DOS攻击,没办法控制使用的用户。此外,我还准备提供一个带用户界面的app。所以我添加了一个MVC服务器,它将创建一个表示层。在微服务架构里,这实现也类似于API网关的模式。

由于微服务不需要大量的web应用程序组件,比如Session或用户管理等,使用Flask或Tornado建立Web应用似乎都是不错的选择。以为最近总是听到Tornado,我对它很好奇,所以选择使用它。关于如何使用Tornado创建Web应用程序,网上有很多例子,其中也包括一些谈论微服务的例子。基于这些示例,再加上最常用的语言是JSON,我编写了以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
i            mport json            
            import tornado.ioloop
            import tornado.web
            from bs4 import BeautifulSoup
            
            class WordsHandler(tornado.web.RequestHandler):
                def get(self):
                    f = open("dice_job_page.html", "r")
                    html = f.read()
                    soup = BeautifulSoup(html, "html5lib")
                    job_desc = soup.find("div", id="jobdescSec")
                    job_text = job_desc.stripped_strings
                    words = ' '.join(job_text)
                    json_response = json.dumps({'data':words})
                    self.write(json_response)
            
            
            def make_app():
                return tornado.web.Application([
                    (r"/api/v1/words", WordsHandler),
                ])
            
            if __name__ == "__main__":
                app = make_app()
                app.listen(8888)
                tornado.ioloop.IOLoop.current().start()

这是最简单的代码,当执行此文件时,响应端口8888上的HTTP GET请求,该服务读取一个本地文件,使用html5lib和BeautifulSoup解析它,并返回JSON包装中的单词。

可以使用curl从命令行测试服务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 $curl http://localhost:8888/api/v1/words

就是这样,我建立了一个微服务。我很兴奋。我几乎完成了!好的,也许它不应该每次从本地文件返回相同的响应。这似乎很容易解决,让我们继续。。

我觉得我需要多增加一些处理逻辑,服务不仅需要接受和响应输入内容,而且作为HTTP服务,它还应该返回至少一个状态代码。而且,每次通过发出请求来测试核心逻辑(提取文本),这看起来很麻烦。最后,虽然这并没有很多代码,但是将函数代码与框架隔离似乎是一个好主意,从而为其他服务设置约定,其中一些服务可能涉及更复杂的逻辑。

最后,我写了另一个文件,看起来是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           def get_words(html):            
              try:
                    soup = BeautifulSoup(html, "html5lib")# (1)
                    job_desc = soup.find("div", id="jobdescSec")# (2)
                    if not job_desc:
                        return None
                    else:
                        job_text = job_desc.stripped_strings# (3)
                        words = ' '.join(job_text)# (4)
                        json_response = json.dumps({'data':words})# (5)
                        return json_response
                except Exception as e:
                    return None
            
            class WordsHandler(tornado.web.RequestHandler):
                def get(self):
                    self.write("Breaking with all conventions, this API does not support GET")
            
                def post(self):
                    html = self.get_argument("html")
                    json_response = get_words(html)
                    if not json_response:
                        self.set_status(HTTP_STATUS_NO_CONTENT, 'There was no content')
                    else:
                        self.write(json_response)
                        self.set_header('Content-Type', 'application/json')
                        self.set_status(HTTP_STATUS_OK)

前面一到五行代码与原始版本完全相同。它们被隔离在一个名为get_words的函数中,该函数可以在不运行Tornado的情况下独立地进行单元测试。在处理程序本身代码中,有一些代码用于返回状态代码并设置其他HTTP头。如果有必要,还可以增加更多。

而设置和启动Tornado的代码则保留在原始文件中。

另外两个用于抓取页面内容和生成word Cloud的服务的代码结构也是大体相同的。

这里展示仅仅是URL抓取的代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            def get_data(url):            
                if not url:
                    return None
                try:
                    response = requests.get(url)
                    response64 = base64.encodebytes(response.content)
                    return response64.decode()
                except Exception as e:
                    return None
            
            class URLHandler(tornado.web.RequestHandler):
                def get(self):
                    url = self.get_argument("url")
                    data = get_data(url)
                    if not data:
                        self.set_status(HTTP_STATUS_NO_CONTENT, 'There was no content')
                    else:
                        self.write({'data': data})
                        self.set_header('Content-Type', 'application/json')
                        self.set_status(HTTP_STATUS_OK)

如果你想知道,为什么response.content是Base64编码的,因为它是一个字节数组,不是直接的JSON可序列化的。

这里是Make Wordcloud 代码。它使用word_cloud项目。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        def get_image(words):        
            if not words:
               return None
            try:
                # Generate a word cloud image using the word_cloud library
                wordcloud = WordCloud(max_font_size=80, width=960, height=540).generate(words)
                plt.imshow(wordcloud, interpolation='bilinear')
                plt.axis("off")
                pf = io.BytesIO()
                plt.savefig(pf, format='jpg')
                jpeg64 = base64.b64encode(pf.getvalue())
                return jpeg64.decode()
            except Exception as ex:
                return None
        
        class WordCloudHandler(tornado.web.RequestHandler):
            def get(self):
                self.write("Breaking with all conventions, this API does not support GET")
        
            def post(self):
                words = self.get_argument("words")
                image = get_image(words)
                if not image:
                    self.set_status(HTTP_STATUS_NO_CONTENT,'There was no content')
                else:
                    self.write(json.dumps({'data':image}))
                    self.set_header('Content-Type', 'application/json')
                    self.set_status(HTTP_STATUS_OK)

微服务建好之后,我只需要创建视图控制器来接收用户提交的url,使用这些微服务构建响应,并向用户发送响应。

我使用Django来构建应用服务器,因为我只想关注我需要的功能,而其他的内容可以由web应用程序来管理。

代码是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            class WordCloudView(TemplateView):            
                template_name = "cloudfun/wordcloud.html"
                form_class = WordCloudForm
            
                def get(self, request, *args, **kwargs):
                    form = self.form_class()
                    return render(request, self.template_name, {'form': form})
            
                def post(self, request, *args, **kwargs):
                    form = self.form_class(request.POST)
                    results = None
                    if form.is_valid():
                        url = form.cleaned_data['urls'].strip()
                        resp = requests.post('http://localhost:8888/api/v1/fromurl',data={'url':url})
                        html = resp.json()['data']
                        resp = requests.post('http://localhost:8887/api/v1/words',data={'html':html})
                        words = resp.json()['data']
                        resp = requests.post('http://localhost:8886/api/v1/wordcloud',data={'words':words})
                        results= resp.json()['data']
            
                    return render(request, self.template_name, {'form': form, 'results': results})

这个视图类的初始版本假设用户只输入了一个URL。这些服务都被hardcode到控制器中(稍后详细介绍)。一个微服务的响应直接插入到下一个微服务中。Django类非常简单,它只有两行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            class WordCloudForm(forms.Form):            
                urls = forms.CharField(.Textarea)

the wordcloud.html 也没模板也很简单

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            {% block content %}            
                Paste URLs into the following:
                <form action="{% url 'wordcloud' %}" method="post">
                    {% csrf_token %}
                    {{ form}}
                <input type="submit" value="OK">
                </form>
                {% if results %}
                    <div><img alt="Embedded Image" 
                              src="data:image/png;base64,{{ results }}" /></div>
                {% endif %}
            {% endblock %}

我现在已经编写了足够的代码来获取用户提交的URL,并向它们显示一个word cloud。是时候检验一下了。

我启动了三个微服务作为后台python任务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            $ python microservices/cloud_creator/api_server.py &
            $ python microservices/fetch_url/api_server.py &
            $ python microservices/dice_scraper /api_server.py &

我在浏览器中启动了Django服务器和页面http://localhost:8000/cloudfun,使用从Dice.com网站获取的URL,然后单击OK。

它工作!

我在浏览器中看到了下面的图片。

从这个简单的微服务示例中,我被微服务的魅力吸引住了。它让我们思考,怎么样将一个大的系统分解成离散的服务,这也就是所谓的关注点分离。我们可以想象,如果您正在构建一个电子商务页面,需要获取商品搜索结果,您可能会启动十几个异步子请求,这些子请求都返回可以组装成一个页面的各种信息数据。在我的脑海里,我想象着一辆F1赛车停在一个维修站,一群工人猛扑上去,然后迅速把它恢复到正常状态,继续前行。

我花费了一个下午的时间完成上面的示例,还有一些代码需要改进。最大的问题是服务的位置被硬编码到视图控制器中。

当然,关注点分离长期以来一直是软件工程关注的焦点。面向对象编程也建议这么做。然后是CORBA,一个由10个IBM工程师组成的团队花了6个月的时间来功能。接下来是web Service和SOAP。当我在2001年为法国电信工作时,我对SOAP进行了评估,可以保证了互操作性。于是我使用Java Web Service来与.Net服务通信。结果发现各式各样的问题,我记得那简直地狱。人们一直在幻想Web服务的扩散,通过使用WSDL编写的服务契约自动被发现。会有航班预订网络服务,金融服务,如果有一个服务瘫痪了,系统就可以查到另一个,令人兴奋的东西。

快进15年,我们来到了微服务领域。但是,从我所看到的情况来看,微服务现在被限制为组织内客户提供服务,而不是对于开放互联网上的任何客户。以后或许微服务会走入互联网。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序你好 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python 学习入门(15)—— Tornado
Tornado,全称Tornado Web Server,是一个用Python语言写成的Web服务器兼Web应用框架,由FriendFeed公司在自己的网站FriendFeed中使用,被Facebook收购以后框架以开源软件形式开放给大众。
阳光岛主
2019/02/19
2.1K0
Tornado入门(七)【认证和安全】
Cookies信息通常不安全,很容易被篡改。如果需要通过cookies来区分不同的登录用户,则需要对cookies进行签名,以防伪造。Tornado通过set_secure_cookie和get_secure_cookie方法支持签名Cookies。为了使用这两个方法,需要在应用中配置cookie_secret。
用户2936342
2018/08/27
4780
Python Web 框架:Tornado
**备注:** Tornado应该运行在类Unix平台,为了达到最佳的性能和扩展性,仅推荐Linux和BSD(充分利用Linux的epoll工具和BSD的kqueue达到高性能处理的目的)
周小董
2019/03/25
1.6K0
Python Web 框架:Tornado
python-tornado
http://docs.pythontab.com/tornado/introduction-to-tornado/index.html
py3study
2020/01/13
7830
Tornado入门(五)应用结构
Tornado web应用的结构通常包含一个或者多个RequestHandler子类,一个将请求转发至处理器的Application对象,以及一个main()函数,用于启动服务器。
用户2936342
2018/08/27
9080
史上最全的AJAX
对于web应用程序:用户浏览器发送请求.服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML),渲染并显示浏览器上·
Wyc
2018/09/11
4.4K0
Python 里最强的Web框架,早就不是Django和Flask了
作者:ConnorZhang 链接:https://juejin.cn/post/6944598601674784775
Python编程与实战
2021/04/14
27.6K0
[译]Tornado web应用的结构
Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5的async/await关键字,并且用旧版本CPython编译Tornado同样可以使用这两个关键字,这无疑是一种进步。其次,这是最后一个支持Python2.6和Python3.2的版本了,在后续的版本了会移除对它们的兼容。现在网络上还没有Tornado4.3的中文文档,所以为了让更多的朋友能接触并学习到它,我开始了这个翻译项目,希望感兴趣的小伙伴可以一起参与翻译,项目地址是tornado-zh on Github,翻译好的文档在Read the Docs上直接可以看到。欢迎Issues or PR。
Jintao Zhang
2018/08/27
9290
前端开发---异步上传文件
有一个名为ajaxFileUpload的JQuery插件可以利用iframe来实现前端页面中异步上传文件。
MiaoGIS
2020/11/25
1.5K0
前端开发---异步上传文件
Python Web 框架:Tornado1.Tornado2.安装3.使用4.Tornado 代码解析
备注: Tornado应该运行在类Unix平台,为了达到最佳的性能和扩展性,仅推荐Linux和BSD(充分利用Linux的epoll工具和BSD的kqueue达到高性能处理的目的)
Python攻城狮
2018/08/23
6330
Python Web 框架:Tornado1.Tornado2.安装3.使用4.Tornado 代码解析
python3.6写一个http接口服务,给别人调用1
首先推荐tornado,Tornado是一个Python web框架和异步网络库,最初在FriendFeed开发。通过使用无阻塞网络I/O,Tornado可以扩展到数万个开放连接,使其成为长轮询、WebSocket和其他需要与每个用户建立长时间连接的应用程序的理想选择。简易而且本地win10能够跑起来。
全栈程序员站长
2022/08/31
1.7K0
python3.6写一个http接口服务,给别人调用1
Tornado 简述
如果你的 python 环境还没有安装 tornado,请直接使用 pip 安装:
IT茂茂
2020/03/05
9270
Http服务传输图片的Python实现
基于tornado框架搭建简单的web服务, 提供post方法, 通过接收image_url字段获取图片的url,通过requests模块获取图片并进行后续的操作。图片的操作重点关注process()方法。
py3study
2020/01/13
5.6K0
Tornado进阶
前面的学习中,我们在创建tornado.web.Application的对象时,传入了第一个参数——路由映射列表。实际上Application类的构造函数还接收很多关于tornado web应用的配置参数
星哥玩云
2022/09/14
1.7K0
Tornado进阶
Tornado基础学习篇
Tornado是使用Python编写的一个强大的、可扩展的Web服务器。它在处理严峻的网络流量时表现得足够强健,但却在创建和编写时有着足够的轻量级,并能够被用在大量的应用和工具中。
py3study
2020/01/20
1.2K0
Tornado(cookie、XSRF、用户验证)
——————–Cookie操作——————– 1、设置Cookie 1、set_cookie(name,value,domain=None,expires=None,path=”/”)
全栈程序员站长
2022/08/31
7700
tornado6与python3.7
On Python 3, IOLoop is always a wrapper around the asyncio event loop. 这是我重新复习tornado的原因,tornado放弃了之前自己实现的tornado.ioloop,全面拥抱asyncio的event_loop.这个改动是非常大的, 而且阅读tornado的源码可以发现其中大部分函数都支持了类型检验,和返回值提示,值得阅读.
py3study
2020/01/02
1.2K0
文末送书|Python写的微服务如何融入Spring Cloud体系?
最近这几天工作和生活都比较忙碌,公众号没有得到及时的更新,还希望各位读者朋友们多多包涵!作为这几天文章更新不及时的弥补,小码哥决定出血给大家赠送一本我的好朋友"@程序员小灰"撰写的《漫画算法》这本书,没有关注“无敌码农”公众号的,关注后回复“小糖糖”就可以参与活动了!已经关注的直接回复“小糖糖”就可以了!
用户5927304
2019/07/30
2.9K0
文末送书|Python写的微服务如何融入Spring Cloud体系?
tornado实现文件下载的代码
获取请求参数;请求参数生成json格式,存入文件;下载json文件 class SpockDataIntegrationDownloadHandler(tornado.web.RequestHandler): def post(self): selectname = self.get_argument('selectname') json_string = {} """ 将请求参数放到dict中 """ type = self.g
用户7705674
2021/11/01
5650
AJAX全套
对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上。
用户1214487
2022/03/26
1.6K0
相关推荐
Python 学习入门(15)—— Tornado
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验