Redis实现简单消息队列

JSF提供了大量之UI标签来简化创建视图。这些UI标签类似于ASP.NET倍受的服务器组件。使用这些标签,可以经其value,binding,action,actionListener等属性直接绑定到托管Bean的性质,实例或者措施齐。

职责异步化

开辟浏览器,输入地点,按下回车,打开了页面。于是一个HTTP请求(request)就由客户端发送到服务器,服务器处理要,返回响应(response)内容。

咱每天都以浏览网页,发送大大小小的请求于服务器。有时候,服务器收到了要,会发现他也得让另外的服务器发送请求,或者服务器也亟需开另外有业务,于是最初们发送的请求虽叫死了,也便是若待服务器就其他的作业。

双重多之早晚,服务器做的额外事情,并不需要客户端等,这时候就足以管这些额外的业务异步去举行。从事异步任务的家伙来不少。主要原理还是拍卖通报消息,针对通知消息通常用是班结构。生产与消费信息进行通信以及事情实现。

1、JSF中之老三百般骨干零部件:

生消费以及队列

上述异步任务之落实,可以抽象为劳动者消费型。如同一个食堂,厨师在起火,吃货在就餐。如果厨师做了重重,暂时发售不完,厨师便会休息;如果客户多,厨师马不停止蹄的大忙,客户则需逐步等待。实现生产者与买主之计因此很多,下面采用Python标准库Queue描绘个小例子:

import random
import time
from Queue import Queue
from threading import Thread

queue = Queue(10)

class Producer(Thread):
    def run(self):
        while True:
            elem = random.randrange(9)
            queue.put(elem)
            print "厨师 {} 做了 {} 饭 --- 还剩 {} 饭没卖完".format(self.name, elem, queue.qsize())
            time.sleep(random.random())

class Consumer(Thread):
    def run(self):
        while True:
            elem = queue.get()
            print "吃货{} 吃了 {} 饭 --- 还有 {} 饭可以吃".format(self.name, elem, queue.qsize())
            time.sleep(random.random())

def main():
    for i in range(3):
        p = Producer()
        p.start()
    for i in range(2):
        c = Consumer()
        c.start()

if __name__ == '__main__':
    main()

盖输出如下:

厨师 Thread-1 做了 1 饭 --- 还剩 1 饭没卖完
厨师 Thread-2 做了 8 饭 --- 还剩 2 饭没卖完
厨师 Thread-3 做了 3 饭 --- 还剩 3 饭没卖完
吃货Thread-4 吃了 1 饭 --- 还有 2 饭可以吃
吃货Thread-5 吃了 8 饭 --- 还有 1 饭可以吃
吃货Thread-4 吃了 3 饭 --- 还有 0 饭可以吃
厨师 Thread-1 做了 0 饭 --- 还剩 1 饭没卖完
厨师 Thread-2 做了 0 饭 --- 还剩 2 饭没卖完
厨师 Thread-1 做了 1 饭 --- 还剩 3 饭没卖完
厨师 Thread-1 做了 1 饭 --- 还剩 4 饭没卖完
吃货Thread-4 吃了 0 饭 --- 还有 3 饭可以吃
厨师 Thread-3 做了 3 饭 --- 还剩 4 饭没卖完
吃货Thread-5 吃了 0 饭 --- 还有 3 饭可以吃
吃货Thread-5 吃了 1 饭 --- 还有 2 饭可以吃
厨师 Thread-2 做了 8 饭 --- 还剩 3 饭没卖完
厨师 Thread-2 做了 8 饭 --- 还剩 4 饭没卖完

a、 JSF容器管理的托管Bean
b、 JSF提供的UI标签,应用界面的UI标签直接绑定到托管Bean
c、 页面导航规则

Redis 队列

Python内置了一个好用之班结构。我们为足以是故redis实现类似的操作。并开一个简单易行的异步任务。

Redis提供了少数种艺术来作消息队列。一个凡是下生产者消费模式模式,另外一个术就是发布订阅者模式。前者会叫一个或多独客户端监听消息队列,一旦消息到达,消费者即时消费,谁先抢到算谁的,如果队列里无音信,则消费者继续监听。后者也是一个或者多独客户端订阅消息频道,只要发布者发布消息,所有订阅者都能收到信,订阅者都是一致之。

脚我们即便来介绍UI标签:

生产消费模式(不建议以)

重在行使了redis提供的blpop获取队列数据,如果队列没有数据则阻塞等待,也尽管是监听。

import redis

class Task(object):
    def __init__(self):
        self.rcon = redis.StrictRedis(host='localhost', db=5)
        self.queue = 'task:prodcons:queue'

    def listen_task(self):
        while True:
            task = self.rcon.blpop(self.queue, 0)[1]
            print "Task get", task

if __name__ == '__main__':
    print 'listen task queue'
    Task().listen_task()

运redis的brpop方式开行,经过一段时间,会意识先后莫名其妙的卡主。也不怕是过程一切ok,redis的lpush也健康,唯独brpop不再花费。该问题十分坏复现,但是连过了一段时间就见面重现。本人使用了高并发,高延迟,弱网络环境等方式试图复现都不曾成功,目前还是当追寻解决方案。目测依赖redis做brokers的排的celery也碰到同样的问题
,并且其他语言也发出相近问题,但是笔者的化解在未适用。猜测问题的故是redis在处理brpop的早晚总是长日子不适用会自动假死。后来应用比较low的方案,每当凌晨3点左右更开一下阵服务。目前装了再度缺少的idle连接时间(config
set timeout 10),再观一下是不是能复现。
无建议于变环境下该方案。如果用类似方案也碰到了问题,并且产生了缓解方案,希望而能够联系自己哈哈哈。
晋升了 redis 3.2 版本后,运行了一个差不多月,目前从来不再出新堵塞的题材。

2、JSF中的UI标签:

颁发订阅模式

使用redis的pubsub功能,订阅者订阅频道,发布者发布信息及频道了,频道就是一个消息队列。

import redis


class Task(object):

    def __init__(self):
        self.rcon = redis.StrictRedis(host='localhost', db=5)
        self.ps = self.rcon.pubsub()
        self.ps.subscribe('task:pubsub:channel')

    def listen_task(self):
        for i in self.ps.listen():
            if i['type'] == 'message':
                print "Task get", i['data']

if __name__ == '__main__':
    print 'listen task channel'
    Task().listen_task()

JSF与JSP的签的分别在,JSF的标签可以下value,binding,action和actionListener等性直接绑定到托管Bean上面去。

Flask 入口

我们分别实现了零星种植异步任务的后端服务,直接开行他们,就能监听redis队列或频道的音信了。简单的测试如下:

import redis
import random
import logging
from flask import Flask, redirect

app = Flask(__name__)

rcon = redis.StrictRedis(host='localhost', db=5)
prodcons_queue = 'task:prodcons:queue'
pubsub_channel = 'task:pubsub:channel'

@app.route('/')
def index():

    html = """
<br>
<center><h3>Redis Message Queue</h3>
<br>
<a href="/prodcons">生产消费者模式</a>
<br>
<br>
<a href="/pubsub">发布订阅者模式</a>
</center>
"""
    return html


@app.route('/prodcons')
def prodcons():
    elem = random.randrange(10)
    rcon.lpush(prodcons_queue, elem)
    logging.info("lpush {} -- {}".format(prodcons_queue, elem))
    return redirect('/')

@app.route('/pubsub')
def pubsub():
    ps = rcon.pubsub()
    ps.subscribe(pubsub_channel)
    elem = random.randrange(10)
    rcon.publish(pubsub_channel, elem)
    return redirect('/')

if __name__ == '__main__':
    app.run(debug=True)

起步脚本,使用

siege -c10 -r 5 http://127.0.0.1:5000/prodcons
siege -c10 -r 5 http://127.0.0.1:5000/pubsub

足分级在监听的剧本输入被见到异步消息。在异步的任务中,可以实施有耗时间的操作,当然目前这些做法并不知道异步的实践结果,如果要理解异步的履结果,可以设想规划协程任务或采用部分工具要RQ或者celery等。

JSF包括个别组标签库:jsf_core.tld核心标签库和html_basic。tld
HTML标签库。

2.1、JSF核心标签库:

f:actionListener
f:attribute
f:convertDateTime
f:convertNumber
f:converter
f:facet
f:loadBundle
f:param
f:phaseListener
f:selectItem
f:selectItems
f:setPropertyActionListener
f:subview
f:validateDoubleRange
f:validateLength
f:validateLongRange
f:validator
f:valueChangeListener
f:verbatim
f:view

至于个标签的事无巨细介绍,可以参照JSF自带的辅文档:

JSF解压目录\tlddocs\index.html

2.2、JSF的HTML标签:

h:commandButton
h:commandLink
h:dataTable
h:form
h:graphicImage
h:inputHidden
h:inputSecret
h:inputText
h:inputTextarea
h:message
h:messages
h:outputFormat
h:outputLabel
h:outputLink
h:outputText
h:panelGrid
h:panelGroup
h:selectBooleanCheckbox
h:selectManyCheckbox
h:selectManyListbox
h:selectManyMenu
h:selectOneListbox
h:selectOneMenu
h:selectOneRadio
h:column

至于个标签的详实介绍,可以参照JSF自带的帮带文档:

JSF解压目录\tlddocs\index.html

2.3、UI标签的通用属性:

JSF的UI大都会扭转一个要多只HTML标签,所以利用这些UI标签有时有属性是通用的:

id
immediate:是否立即处理UIInput组件和实现了ActionSource接口的UI组件上事件
rendered:指定条件表达式,当条件表达式为true时才会绘制组件
required:指定用户是否必须为组件输入值
requeredMessage:与requered一起使用,如果没有输入时提示信息
value:该组件的值绑定到托管对象上
onblur
onchange
onclick
ondblclick
onfocus
onkeydown
onkeypress
onkeyup
onmousedown
onmousemove
onmouseout
onmouseover
onmouseup
style
styleClass
binding:将组件本身绑定到托管Bean

2.4、<f:view>标签的常用属性:

locale:点名国家语言
renderKitId:JSF根据该属性值选择相应的绘制器工具箱来绘制该页面
beforePhase:绑定生命周期监听器(必须产生public void
beforePhase(Java.faces.event.PhaseEvent)形式之签名),JSF会在每个生命周期阶段(除了回复视图)之前调用该方法。
afterPhase:绑定生命周期监听器(必须来public void
afterPhase(java.faces.event.PhaseEvent)形式之签名),JSF会在每个生命周期阶段(除了回复视图)之后调用该办法。

3、UI标签的使用举例:

3.1、<h:form>和核心输入标签的采用:

<f:view>
    <h1>表单标签</h1>
    <h:form>
        单行文本框:<h:inputText value="#{userbean.username }" /><br />
        密码框:<h:inputSecret value="#{userbean.password }" /><br />
        多行文本区:<h:inputTextarea rows="3" cols="20" /><br />
        隐藏域:<h:inputHidden value="#{userbean.message }" /><br />
    </h:form>
</f:view>

里头<h:inputText>和<h:inputSecret>可以指定一个size属性用于指定输入框的绝充分长。

3.2、多选择标签的使:

<h1>多选标签的使用</h1>
<h:form>
    <!-- 复选框 -->
    <h:selectManyCheckbox value="#{userbean.booklist }">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectManyCheckbox>

    <!-- 生成一个允许多选的列表框 -->
    <h:selectManyListbox value="#{userbean.booklist }" size="5">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectManyListbox>

    <!-- 生成一个允许多选的复合框 -->
    <h:selectManyMenu value="#{userbean.booklist }">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectManyMenu>
</h:form>

下方面的老三只标签须与<f:selectItem>或者<f:selectItems>标签结合使用,其中的<f:selectItem>3独特性的含义:

itemLabel:可视化标签值
itemValue:点名生成列表项或者复选框的价值
value:同其它的UI标签的value属性不同,不是以欠零件的价绑定到托管Bean,而是将拖欠器件本身绑定到托管Bean

马上三单标签的value属性值必须是一个List或者数组。

3.3、单选取标签的施用:

<h1>单选标签的使用</h1>
<h:form>
    <!-- 生成一组单选按钮 -->
    <h:selectOneRadio value="userbean.booklist">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectOneRadio>
    <!-- 生成一个只允许单选的列表框 -->
    <h:selectOneListbox value="userbean.booklist" size="5">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectOneListbox>
    <!-- 生成一个只允许单选的下来菜单 -->
    <h:selectOneMenu value="#{userbean.booklist }">
        <f:selectItem itemLabel="Core Java" itemValue="Java" />
        <f:selectItem itemLabel="Thinking in C++" itemValue="C++" />
        <f:selectItem itemLabel="Spring Internals" itemValue="Spring" />
    </h:selectOneMenu>
</h:form>

顿时三独标签及前边介绍的老三单标签的效果基本相似,只是这里的只能单选。

3.4、UISelectBoolean组件的动:

<h:selectBooleanCheckbox />

以页面及大成一个复选框,用于勾选或者取消勾选该复选框

与地方三只复选框不同,它的value属性必须绑定到托管Bean中boolean类型的性,不待同<f:selectItem>标签一起利用。

3.5、UICommand组件的用:

<h1>UICommand组件的使用</h1>
<h:form>
    <!-- 生成一个可以提交表单的按钮 -->
    <h:commandButton value="点击" />
    <!-- 生成一个图片按钮 -->
    <h:commandButton image="images/01.jpg" />
    <!-- 生成一个可以提交表单的超链接 -->
    <h:commandLink value="提交表单" />
    <!-- 生成一个可以提交表单的图片链接 -->
    <h:commandLink shape="circle" coords="20,20,10">
        <img src="images/01.jpg" />
    </h:commandLink>
</h:form>

3.6、UIOutput对应的输出组件的动:

<h1>UIOutput对应的输出组件的使用</h1>
<!-- 使用outputText标签输出国际化资源 -->
<h:outputText value="#{userInfo.username }" />
<!-- 使用outputText标签输出Bean属性 -->
<h:outputText value="#{userbean.username }" />
<!-- 生成Label标签 -->
<h:outputLabel value="#{userbean.username }" />
<!-- 生成超链接 -->
<h:outputLink value="http://www.itzhai.com">IT宅</h:outputLink>
<!-- 输出带占位符的国际化消息 -->
<h:outputFormat value="#{userInfo.message }">
    <f:param value="arthinking" />
</h:outputFormat>

这里运用到了国际化资源,需要创造:

每当faces-config.xml问价中加载国际化资源的布置:

<application>
    <resource-bundle>
        <base-name>com.itzhai.user</base-name>
        <var>userInfo</var>
    </resource-bundle>
</application>

然后以com.itzhai目录下开创一个国际化资源文件之basename为user:

username="arthinking"
message=用户名:{0}

3.7、panelGrid和panelGroup的使用:

<h1>panelGrid标签的使用</h1>
    <h:panelGrid columns="3" width="300px" border="1">
        <!-- 生成表格标题 -->
        <f:facet name="caption">
            <h:outputText value="表格标题" />
        </f:facet>
        <!-- 生成表格头 -->
        <f:facet name="header">
            <h:outputText value="表格头" />
        </f:facet>
        <h:outputText value="1" />
        <h:outputText value="2" />
        <h:outputText value="3" />
        <!-- panelGroup的使用 -->
        <h:panelGroup layout="block" style="color:#cfcfff">
            <h:outputText value="4" />
            <h:outputText value="5" />
        </h:panelGroup>
        <h:outputText value="6" />
        <h:outputText value="7" />
    </h:panelGrid>

panelGrid用于转移表格,panelGroup用于把多单要素构成一个要素。

3.8、使用UIData迭代输出从数据源中获取之数据:

<h1>dataTable的使用</h1>
<h:dataTable width="600px" border="1" value="showbook.bookList" var="book" rowClasses="odd,even">

    <!-- 使用facet生成caption -->
    <f:facet name="caption">
        <h:outputText value="book list" />
    </f:facet>

    <!-- 定义第一列 -->
    <h:column>
        <f:facet name="header">
            <h:outputText>图书名</h:outputText>
        </f:facet>
        <h:inputText value="#{book.name }" size="3" />
    </h:column>
    <!-- 定义第二列 -->
    <h:column>
        <f:facet name="header">
            <h:outputText>图书分类</h:outputText>
        </f:facet>
        <h:outputLink value="#{book.url }">
            <h:inputText value="#{book.bookClass }" />
        </h:outputLink>
    </h:column>

    <!-- 生成表格底部 -->
    <f:facet name="footer">
        <h:panelGroup>
            <h:outputText value="计算机书籍" />
        </h:panelGroup>
    </f:facet>
</h:dataTable>

下用以号称吧showbook的托管Bean中提供一个bookList数据源,可以以他的get方法被提供数据:

public List<BookInfo> getBookList(){
    List<BookInfo> books = new ArrayList<BookInfo>();
    books.add(new BookInfo("Core Java", "Java", "www.itzhai.com"));
    books.add(new BookInfo("Core Java", "Java", "www.itzhai.com"));
    books.add(new BookInfo("Core Java", "Java", "www.itzhai.com"));
    return books;
}

其中的BookInfo类如下:

public class BookInfo {

    private String name;
    private String url;
    private String bookClass;

    public BookInfo(String name, String bookClass, String url){
        this.name = name;
        this.bookClass = bookClass;
        this.url = url;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getBookClass() {
        return bookClass;
    }
    public void setBookClass(String bookClass) {
        this.bookClass = bookClass;
    }
}

3.9、图像组件的施用:

<h:graphicImage value="images/01.jpg" alt="图像01" />

这个标签将生成HTML的img标签。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图