本发明提供一种氮氧化物甲烷选择催化还原催化剂。该催化剂以可溶性镓盐和可溶性铈盐为原料,以分子筛为载体,通过有机酸辅助浸渍法制备。催化剂的质量百分比组成为:
金属ga2~5%
金属ce1~4%
分子筛91~97%
其中,所述的分子筛为具有mfi、bea、cha结构的硅铝分子筛和硅磷铝分子筛。
优选的,所述的分子筛为zsm-5分子筛,si/al=25~35。
所述的有机酸为柠檬酸、谷氨酸或水杨酸中的任意一种,且摩尔比为有机酸:(ce+ga)=0~1.5。
优选的,所述的有机酸为柠檬酸,且摩尔比为有机酸:(ce+ga)=1。
进一步优选的,h2o:h-zsm-5=100质量比、ce:h-zsm-5=0.02质量比、ga:h-zsm-5=0.04质量比,柠檬酸:(ga+ce)=1摩尔比。
所述的可溶性镓盐为硝酸镓、氯化镓,可溶性铈盐为硝酸铈、氯化铈、硫酸铈、醋酸铈。
本发明提供一种氮氧化物甲烷选择催化还原催化剂,其制备方法包括以下步骤:
(1)将有机酸溶于计量水中,然后加入计量的可溶性镓盐和可溶性铈盐,剧烈搅拌0.3~2小时后,加入计量的分子筛载体,继续搅拌6-24h后,得到均匀混合浆液。
(2)将混合浆液低温蒸干,并于80~120℃干燥6-20小时;
(3)将步骤(2)所得产品在10%h2/ar中300~800℃还原0.5~2小时,再在10%o2/ar中200~500℃氧化0.5~2小时;
(4)将步骤(3)所得产品直接挤压成型获得成型催化剂,或者涂覆于具有固定形状的堇青石陶瓷或金属波纹板上获得整体式催化剂。
本发明提供一种氮氧化物甲烷选择催化还原催化剂,其应用方法包括以下步骤:
(1)将成型催化剂或整体式催化剂放入固定床反应器中,在10%o2/ar气氛中300~400℃预处理1小时;
(2)将反应器温度升温至300-600℃进行ch4-scr反应,反应入口氮氧化物浓度为100~2000ppm,甲烷/氮氧化物摩尔比为1~3,反应的体积空速为1000~100000h-1。
优选的,反应的体积空速为40000~60000h-1。甲烷/氮氧化物摩尔比为1.45。
本发明提供的氮氧化物甲烷选择催化还原催化剂,可用于固定源氮氧化物的配方处理,如火电厂、钢铁厂、水泥厂等锅炉的烟气脱硝过程。
本发明提供了一种氮氧化物甲烷选择催化还原催化剂,在ch4-scr反应中具有高活性和稳定性。该催化剂制备过程简单易操作,反应寿命长。
与现有技术相比,本发明还具有以下优点和效果:
(1)催化剂表现出优异的氮氧化物净化效果,氮氧化物净化效率最高可达95%以上;
(2)催化剂组分环境友好且不含贵金属,制备成本较低;
(3)水蒸气对催化剂反应活性的影响可逆,除水后活性可以恢复。
附图说明
图1为柠檬酸辅助浸渍法制备的ce-ga/h-zsm-5催化剂的xrd谱图;
图2a为h-zsm-5催化剂的sem照片;
图2b为柠檬酸辅助浸渍法制备的ce-ga/h-zsm-5催化剂的sem照片;
图3为柠檬酸辅助浸渍法制备的ce-ga/h-zsm-5催化剂的ch4-scr反应活性曲线图;
图4为ce-ga/h-zsm-5催化剂在ch4-scr反应中的水蒸气稳定性测试图;
图5a为3%单组分ga/h-zsm-5催化剂的ch4-scr反应活性曲线图;
图5b为4%单组分ga/h-zsm-5催化剂的ch4-scr反应活性曲线图;
图5c为5%单组分ga/h-zsm-5催化剂的ch4-scr反应活性曲线图。
具体实施方式
以下对本发明的具体实施方式进行详细说明。应当理解的是,此处所描述的具体实施方式仅用于说明和解释本发明,并不用于限制本发明。
在本文中所披露的范围的端点和任何值都不限于该精确的范围或值,这些范围或值应当理解为包含接近这些范围或值的值。对于数值范围来说,各个范围的端点值之间、各个范围的端点值和单独的点值之间,以及单独的点值之间可以彼此组合而得到一个或多个新的数值范围,这些数值范围应被视为在本文中具体公开。
本发明中的技术术语,给出定义的从其定义,未给出定义的则按本领域的通常含义理解。
实施例1:
(1)ce-ga/h-zsm-5催化剂的制备
原料配比如下:h2o:h-zsm-5=100(质量比)、ce:h-zsm-5=0.02(质量比)、ga:h-zsm-5=0.04(质量比),柠檬酸:(ga+ce)=1(摩尔比)
将计量的柠檬酸溶于水中,加入计量的硝酸镓、硝酸铈,剧烈搅拌0.5小时后,加入h-zsm-5(si/al=28)粉末,继续搅拌24小时,得到均匀混合浆液;将混合浆液低温蒸干,并于90℃烘箱中干燥15小时;将干燥后的样品在10%h2/ar中650℃还原1小时,在10%o2/ar中350℃氧化1小时,冷却至室温后将样品直接挤压成型。
(2)ce-ga/h-zsm-5催化剂在ch4-scr反应中的应用
称取0.12g20-40目成型催化剂,放入固定床反应器中,在10%o2/ar气氛中350℃预处理1小时后,调整反应器温度为300℃,并通入反应混合气,反应体积空速为60000h-1,平衡气各组分含量如下:no=2750ppm,ch4=4000ppm,即甲烷/氮氧化物摩尔比约为1.45,o2=4%,h2o=6%,he为平衡气;每50℃检测反应过程中的一个数据点,记录温度范围为300-550℃,用nox分析仪(ecotechec9841)和气相色谱仪记录不同温度下的反应数据,结果如图3所示。
催化剂结果表明,氮氧化物的转化率随着反应温度的升高而升高,当反应温度超过450℃时,氮氧化物生成氮气的转化率接近90%,550℃时可达到95%以上,如表1所示。
实施例2:
按照实施例1中的原料与步骤,不同的是,柠檬酸的使用量为0,即不加入有机酸;按照实施例1中催化剂在ch4-scr反应的应用方法,结果表明,550℃时氮氧化物生成氮气的转化率约为77%,如表1所示。
实施例3:
按照实施例1中的原料与步骤,不同的是,固定ga的含量为4%,调变金属ce的负载量为1%,按照实施例1中催化剂在ch4-scr反应的应用方法,结果表明,550℃时氮氧化物生成氮气的转化率为70%。
实施例4:
按照实施例1中的原料与步骤,不同的是,将辅助浸渍的有机酸变为谷氨酸和水杨酸;按照实施例1中催化剂在ch4-scr反应的应用方法,结果如表1所示。从表中可以看出,使用有机酸辅助浸渍,可以明显提高催化剂的催化活性,其中,柠檬酸的提升效果最好,550℃时可由不添加有机酸时的77%提高至最高95%左右,其次为谷氨酸,最高可达到84%,水杨酸效果最差,仅提高2%左右。
表1:有机酸对双金属ce-ga/h-zsm-5催化剂nox转化率的影响
实施例5:
按照实施例1中的原料与步骤,不同的是,将柠檬酸/金属(ga+ce)摩尔比调节为0.5和1.5;按照实施例1中催化剂在ch4-scr反应的应用方法,结果如表2所示。当柠檬酸/金属(ga+ce)的摩尔比为0.5时,550℃氮氧化物生成氮气的转化率可达到83%;当柠檬酸/金属(ga+ce)摩尔比为1.5时,550℃氮氧化物生成氮气的转化率亦可达到80%。但均小于柠檬酸/金属(ga+ce)摩尔比为1的转化率。
表2:柠檬酸用量对双金属ce-ga/h-zsm-5催化剂nox转化率的影响
实施例6:
按照实施例1中的原料与步骤,不同的是,将分子筛载体变为cha结构分子筛,即h-ssz-13(si/al=24)分子筛;按照实施例1中催化剂在ch4-scr反应的应用方法,结果表明,550℃时氮氧化物生成氮气的转化率约为82%,小于采用h-zsm-5分子筛的转化率。
实施例7:
按照实施例1中的原料与步骤,得到2%ce-4%ga/h-zsm-5催化剂;按照实施例1中催化剂在ch4-scr反应的应用方法,不同的是,反应体积空速分别调整为40000h-1和90000h-1,结果表明,550℃时氮氧化物生成氮气的转化率分别为90%和72%。
实施例8:
按照实施例1中的原料与步骤,得到2%ce-4%ga/h-zsm-5催化剂;按照实施例1中催化剂在ch4-scr反应的应用方法,不同的是,反应混合气中,ch4组分的含量调整为2750ppm和5500ppm,即甲烷/氮氧化物摩尔比调整为1和2,结果表明,550℃时氮氧化物生成氮气的转化率分别为85%和81%。
实施例9:催化剂的稳定性实验
按照实施例1中的原料与步骤,得到2%ce-4%ga/h-zsm-5催化剂;按照实施例1中催化剂在ch4-scr反应的应用方法,不同的是,反应混合气中,h2o组分的含量调整为0或6%,以此考察水蒸气对催化剂性能的影响。
图4为ce-ga/h-zsm-5催化剂在ch4-scr反应中的水蒸气稳定性测试。从图中可以看出,当反应体系中不存在水蒸气时,在最初30小时内no转化为n2的转化率保持在约92%;当在反应体系中引入6%水蒸气后,no转化为n2的转化率仅降至约89%,并可保持150小时;当再次去除混合气中的水蒸气后,no转化为n2的转化率基本可恢复至无水状态的水平。结果表明,水蒸气对该催化剂反应活性的影响可逆,该催化剂具有优异的稳定性。
对比例1:单组分ga/h-zsm-5催化剂的ch4-scr活性
按照实施例1中的原料与步骤,不同的是,仅添加金属ga而不加入金属ce,按照ga:h-zsm-5=0.03、0.04和0.05(质量比)的配比,制得金属ga负载量分别为3%、4%和5%的单组分ga/h-zsm-5催化剂;按照实施例1中催化剂在ch4-scr反应的应用方法,结果如图5所示,可以看出,no转化为n2的最高转化率分别为59%、49%和52%,远低于双组分ce-ga/h-zsm-5催化剂的最高转化率。
完整全部详细技术资料下载
这种问题其实到官方文档上查看一番就可以知道,tomcat很早的版本还是使用的BIO,之后就支持NIO了,具体版本我也不记得了,有兴趣的自己可以去查下。本篇的tomcat版本是tomcat8.5。可以到这里看下 tomcat8.5的配置参数
我们先来简单回顾下目前一般的NIO服务器端的大致实现,借鉴infoq上的一篇文章 Netty系列之Netty线程模型 中的一张图
所以一般参数就是Acceptor线程个数,Worker线程个数。来具体看下参数
文档描述为:
The maximum queue length for incoming connection requests when all possible request processing threads are in use. Any requests received when the queue is full will be refused. The default value is 100.
这个参数就立马牵涉出一块大内容:TCP三次握手的详细过程,这个之后再详细探讨(操作系统的接收队列长度默认为100)。这里可以简单理解为:连接在被ServerSocketChannel accept之前就暂存在这个队列中,acceptCount就是这个队列的最大长度。ServerSocketChannel accept就是从这个队列中不断取出已经建立连接的的请求。所以当ServerSocketChannel accept取出不及时就有可能造成该队列积压,一旦满了连接就被拒绝了
文档如下描述
The number of threads to be used to accept connections. Increase this value on a multi CPU machine, although you would never really need more than 2. Also, with a lot of non keep alive connections, you might want to increase this value as well. Default value is 1.
Acceptor线程只负责从上述队列中取出已经建立连接的请求。在启动的时候使用一个ServerSocketChannel监听一个连接端口如8080,可以有多个Acceptor线程并发不断调用上述ServerSocketChannel的accept方法来获取新的连接。参数acceptorThreadCount其实使用的Acceptor线程的个数。
文档描述如下
The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will accept, but not process, one further connection. This additional connection be blocked until the number of connections being processed falls below maxConnections at which point the server will start accepting and processing new connections again. Note that once the limit has been reached, the operating system may still accept connections based on the acceptCount setting. The default value varies by connector type. For NIO and NIO2 the default is 10000. For APR/native, the default is 8192.
Note that for APR/native on Windows, the configured value will be reduced to the highest multiple of 1024 that is less than or equal to maxConnections. This is done for performance reasons. If set to a value of -1, the maxConnections feature is disabled and connections are not counted.
这里就是tomcat对于连接数的一个控制,即最大连接数限制。一旦发现当前连接数已经超过了一定的数量(NIO默认是10000,BIO是200与线程池最大线程数密切相关),上述的Acceptor线程就被阻塞了,即不再执行ServerSocketChannel的accept方法从队列中获取已经建立的连接。但是它并不阻止新的连接的建立,新的连接的建立过程不是Acceptor控制的,Acceptor仅仅是从队列中获取新建立的连接。所以当连接数已经超过maxConnections后,仍然是可以建立新的连接的,存放在上述acceptCount大小的队列中,这个队列里面的连接没有被Acceptor获取,就处于连接建立了但是不被处理的状态。当连接数低于maxConnections之后,Acceptor线程就不再阻塞,继续调用ServerSocketChannel的accept方法从acceptCount大小的队列中继续获取新的连接,之后就开始处理这些新的连接的IO事件了
文档描述如下
The maximum number of request processing threads to be created by this Connector, which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200. If an executor is associated with this connector, this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool.
这个简单理解就算是上述worker的线程数,下面会详细的说明。他们专门用于处理IO事件,默认是200。
上面参数仅仅是简单了解了下参数配置,下面我们就来详细研究下tomcat的NIO服务器具体情况,这就要详细了解下tomcat的NioEndpoint实现了
先来借鉴看下 tomcat高并发场景下的BUG排查 中的一张图
这张图勾画出了NioEndpoint的大致执行流程图,worker线程并没有体现出来,它是作为一个线程池不断的执行IO读写事件即SocketProcessor(一个Runnable),即这里的Poller仅仅监听Socket的IO事件,然后封装成一个个的SocketProcessor交给worker线程池来处理。下面我们来详细的介绍下NioEndpoint中的Acceptor、Poller、SocketProcessor
获取指定的Acceptor数量的线程
可以看到就是一个while循环,循环里面不断的accept新的连接。
先来看下在accept新的连接之前,首选进行连接数的自增,即countUpOrAwaitConnection
当我们设置maxConnections=-1的时候就表示不用限制最大连接数。默认是限制10000,如果不限制则一旦出现大的冲击,则tomcat很有可能直接挂掉,导致服务停止。
这里的需求就是当前连接数一旦超过最大连接数maxConnections,就直接阻塞了,一旦当前连接数小于最大连接数maxConnections,就不再阻塞,我们来看下这个功能的具体实现latch.countUpOrAwait()
具体看这个需求无非就是一个共享锁,来看具体实现:
目前实现里算是使用了2个锁,LimitLatch本身的AQS实现再加上AtomicLong的AQS实现。也可以不使用AtomicLong来实现。
共享锁的tryAcquireShared实现中,如果不依托AtomicLong,则需要进行for循环加CAS的自增,自增之后没有超过limit这里即maxConnections,则直接返回1表示获取到了共享锁,如果一旦超过limit则首先进行for循环加CAS的自减,然后返回-1表示获取锁失败,便进入加入同步队列进入阻塞状态。
共享锁的tryReleaseShared实现中,该方法可能会被并发执行,所以释放共享锁的时候也是需要for循环加CAS的自减
上述的for循环加CAS的自增、for循环加CAS的自减的实现全部被替换成了AtomicLong的incrementAndGet和decrementAndGet而已。
上文我们关注的latch.countUpOrAwait()方法其实就是在获取一个共享锁,如下:
从上面可以看到在真正获取一个连接之前,首先是把连接计数先自增了。一旦TCP三次握手成功连接建立,就能从ServerSocketChannel的accept方法中获取到新的连接了。一旦获取连接或者处理过程发生异常则需要将当前连接数自减的,否则会造成连接数虚高,即当前连接数并没有那么多,但是当前连接数却很大,一旦超过最大连接数,就导致其他请求全部阻塞,没有办法被ServerSocketChannel的accept处理。该bug在Tomcat7.0.26版本中出现了,详细见这里的一篇文章 Tomcat7.0.26的连接数控制bug的问题排查
然后我们来看下,一个SocketChannel连接被accept获取之后如何来处理的呢?
处理过程如下:
下面就来详细介绍下Poller
前面没有说到Poller的数量控制,来看下
如果不设置的话最大就是2
来详细看下getPoller0().register(channel):
就是轮训一个Poller来进行SocketChannel的注册
这里又是进行一些参数包装,将socket和Poller的关系绑定,再次从缓存中取出或者重新构建一个PollerEvent,然后将该event放到Poller的事件队列中等待被异步处理
在Poller的run方法中不断处理上述事件队列中的事件,直接执行PollerEvent的run方法,将SocketChannel注册到自己的Selector上。
并将Selector监听到的IO读写事件封装成SocketProcessor,交给线程池执行
我们来看看这个线程池的初始化:
就是创建了一个ThreadPoolExecutor,那我们就重点关注下核心线程数、最大线程数、任务队列等信息
核心线程数最大是10个,再来看下最大线程数
默认就是上面的配置参数maxThreads为200。还有就是TaskQueue,这里的TaskQueue是LinkedBlockingQueue<Runnable>的子类,最大容量就是Integer.MAX_VALUE,根据之前ThreadPoolExecutor的源码分析,核心线程数满了之后,会先将任务放到队列中,队列满了才会创建出新的非核心线程,如果队列是一个大容量的话,也就是不会到创建新的非核心线程那一步了。
但是这里的TaskQueue修改了底层offer的实现
这里当线程数小于最大线程数的时候就直接返回false即入队列失败,则迫使ThreadPoolExecutor创建出新的非核心线程。
TaskQueue这一块没太看懂它的意图是什么,有待继续研究。
本篇文章描述了tomcat8.5中的NIO线程模型,以及其中涉及到的相关参数的设置。
Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
无 选择器
面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来逗选择地通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
NIO和IO如何影响应用程序的设计
无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:
1.对NIO或IO类的API调用。
2.数据处理。
3.用来处理数据的线程数。
API调用
当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
数据处理
使用纯粹的NIO设计相较IO设计,数据处理也受到影响。
在IO设计中,我们从InputStream或 Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:
Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890
该文本行的流可以这样处理:
BufferedReader reader = new BufferedReader(new InputStreamReader(input))
String nameLine = reader.readLine()
String ageLine = reader.readLine()
String emailLine = reader.readLine()
String phoneLine = reader.readLine()
请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完, readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:
(Java IO: 从一个阻塞的流中读数据) 而一个NIO的实现会有所不同,下面是一个简单的例子:
ByteBuffer buffer = ByteBuffer.allocate(48)
int bytesRead = inChannel.read(buffer)
注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,逗Name:An地,你能处理数据吗看显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。
所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢看好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:
ByteBuffer buffer = ByteBuffer.allocate(48)
int bytesRead = inChannel.read(buffer)
while(! bufferFull(bytesRead) ) {
bytesRead = inChannel.read(buffer)
}
bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。
bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。
如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了逗缓冲区数据循环就绪地:
3) 用来处理数据的线程数
NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如
Java NIO: 单线程管理多个连接
如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:
Java IO: 一个典型的IO服务器设计- 一个连接通过一个线程处理
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)