公司建设门户网站的总结,网站开发策划书怎么写,做淘客网站要什么样服务器,网站建设用那个软件Redis Lua脚本的详细介绍以及使用入门。 文章目录Redis Lua脚本的引入开源软件的可扩展性Redis的扩展性脚本Redis Lua脚本的基本使用通过EVAL命令执行Lua脚本通过脚本与Redis交互Java中调用Redis Lua脚本Java调用Lua脚本的方式Redis Lua脚本的使用建议脚本缓存脚本缓存稳定性脚… Redis Lua脚本的详细介绍以及使用入门。 文章目录Redis Lua脚本的引入开源软件的可扩展性Redis的扩展性脚本Redis Lua脚本的基本使用通过EVAL命令执行Lua脚本通过脚本与Redis交互Java中调用Redis Lua脚本Java调用Lua脚本的方式Redis Lua脚本的使用建议脚本缓存脚本缓存稳定性脚本参数化脚本超时处理Redis集群中Lua脚本的使用其他限制Redis 7 FunctionsLua脚本相关常用命令总结参考本次的主要分享主题是Redis Lua脚本的使用入门主要分为三个方面
Redis Lua脚本的介绍和基本使用语法。Java如何中调用Lua脚本操作Redis。Lua脚本的相关使用建议。
Redis Lua脚本的引入
开源软件的可扩展性
流行的开源软件或者框架都会非常注重一个特性那就是“可扩展性”或者说“可编程性”。
例如Spring框架就提供了非常多的扩展点各种PostProcessor、Aware、Lifecycle等等扩展接口让开发者能够自定义程序运行的逻辑。
又比如Dubbo它的插件式架构的精髓就在于开发者可以基于它的SPI机制任意的替换例如Protocol、LoadBalance等等几乎所有的Dubbo组件。
一个框架或者软件的可扩展性够强往往意味着其足够的灵活能够满足更多用户的定制化需求或者说二次开发的需求进而影响到其在开源市场的占有率。
Redis的扩展性脚本
Redis作为最常见的缓存数据库它的大多数命令专门用于以不同的方式操作不同的核心数据类型这些命令并不能在一次调用过程中组合使用。
Redis中的可扩展性可编程性实际上就是指服务器能够执行任意用户定义的逻辑片段我们把这样的逻辑片段称为脚本Redis在2.6.0版本开始引入脚本的功能。 用户编写的脚本实际上在Redis中由一个嵌入式执行引擎执行。目前Redis只支持一个脚本引擎即Lua 5.1解释器但是计划在未来支持更多的脚本引擎。
Redis Lua脚本的好处有很多最为人熟知的亮点包括
很多复杂的业务需要包含多步Redis操作这些操作常被拆分为一个个单独的API方法。开发人员可以使用Redis脚本的功能实现一个健壮的、特定于应用程序的api。这样的api可以封装业务逻辑并跨多个键和不同的数据结构维护数据模型。复杂业务的多步Redis操作一般情况下需要多次与Redis服务器交互需要多次网络IO开销。使用一个Redis脚本来完成多步操作的封装然后发送给Redis服务器执行因为脚本在同一个服务器中执行所以从脚本中读取和写入数据非常有效仅需要少量的网络IO的开销给我们带来更好的性能。Redis脚本和Redis命令一样它被Redis服务器端单线程的执行这样就具备了天然的原子性常常可以被用于实现基于Redis的分布式锁。
Redis Lua脚本的基本使用
Lua 是一种轻量小巧的嵌入式脚本语言用标准C语言编写并以源代码形式开放 其设计目的是为了嵌入其他应用程序中从而为应用程序提供灵活的扩展和定制功能。常见应用如游戏开发中的脚本。
关于Lua的基本语法实际上比较的简单在此不做赘述。下面介绍如何在Redis中执行Lua脚本。
通过EVAL命令执行Lua脚本
我们可以使用EVAL命令在服务器上执行自定义Lua脚本。例如
EVAL return Hello, scripting! 0
Hello, scripting!该命令要求返回Hello, scripting!字符串。
EVAL命令的完整格式为
EVAL script numkeys [key [key ...]] [arg [arg ...]]scriptLua脚本。numkeys指定KEYS[]参数的数量非负整数。KEYS[]传入的Redis键参数。ARGV[]传入的脚本参数。KEYS[]与ARGV[]的索引均从1开始。
在上面的案例中我们使用参数值0表示该Lua脚本需要0个KEYS参数。
Lua脚本中支持动态传递参数执行上下文通过KEYS和ARGV全局运行时变量来传递使脚本使用的参数。KEYS表示预先填充了脚本执行前提供给脚本的所有键名参数而ARGV则用于类似的目的但通常用于预先填充常规参数。
注意KEYS和ARGV数组的参数取值从1开始。
例如一个较为完整的EVAL命令的格式例如
EVAL return { KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3] } 2 key1 key2 arg1 arg2 arg31) key12) key23) arg14) arg25) arg3该命令表示返回一个数组内部的元素均来自于KEYS和ARGV数组参数。
通过脚本与Redis交互
redis单例是一个所有脚本中都可以访问的对象实例。它提供了从脚本与Redis交互的API。
在redis对象的函数中最常见的调用的函数就是redis.call()该函数将会调用给定的Redis命令并返回其应答他的语法如下
redis.call(command [,arg...])commandredis命令必须。arg动态传递的参数可选。arg支持通过KEYS和ARGV动态传递。
例如以下命令要求Redis服务端设置一个key为zhuzhanvalue为v3的缓存
EVAL return redis.call(SET, KEYS[1], ARGV[1]) 1 zhuzhan v3
OK以下命令要求从Redis服务端获取key为zhuzhan的缓存
EVAL return redis.call(get, KEYS[1]) 1 zhuzhan
v3更复杂一些的我们实现“判断如果某个key不存在则设置它”的功能也就是常见的分布式锁获取锁
EVAl return redis.call(set,KEYS[1], ARGV[1], ARGV[2], ARGV[3] , ARGV[4]) 1 lock lock nx px 60000
OK然后我们实现“如果存在某个key并且值相等则删除key”的功能也就是分布式锁的解锁
EVAL if (redis.call(get,KEYS[1])ARGV[1]) then return redis.call(DEL,KEYS[1])
end return 0
1 x y1更多操作参见Lua APIhttps://redis.io/docs/manual/programmability/lua-api/
Java中调用Redis Lua脚本
Java调用Lua脚本的方式
上面我们学习了在Redis客户端中直接执行Lua脚本的方法但是更常见的我们是直接通过Java客户端操作Redis那么Java如何通过Lua脚本操作Redis呢
实际上通过Java客户端调用Lua脚本相比于命令行客户端更加简单。
比如最常见的Jedis客户端我们可以调用jedis#eval方法执行Lua脚本例如
public static final String prefix test:;
public static final List KEYS Collections.singletonList(prefix jedis);
public static final List ARGV Stream.of(jedis, nx, px, 60000).collect(Collectors.toList());Object eval jedis.eval(return redis.call(set,KEYS[1], ARGV[1], ARGV[2], ARGV[3] , ARGV[4]), KEYS, ARGV);
System.out.println(eval);另外如果是springboot项目那么更加推荐使用redisTemplate客户端来执行Lua脚本。
首先我们需要创建一个DefaultRedisScript对象该对象表示Lua脚本的抽象。然后设置设置DefaultRedisScript的返回值类型支持List、Boolean、Long、String类型默认是返回String类型。然后将Lua脚本代码可以直接设置进去也可以选择从文件中加载。最后通过redisTemplate#execute方法执行脚本获取结果。
DefaultRedisScriptBoolean objectDefaultRedisScript new DefaultRedisScript();
//返回值类型
objectDefaultRedisScript.setResultType(Boolean.class);
//直接设置Lua
//objectDefaultRedisScript.setScriptText(return redis.call(set,KEYS[1], ARGV[1], ARGV[2], ARGV[3] , ARGV[4]));
//设置Lua资源
objectDefaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(/lua/lock.lua)));
//执行脚本
Boolean execute redisTemplate.execute(objectDefaultRedisScript, KEYS, ARGV.toArray());
System.out.println(execute);Redis Lua脚本的使用建议
脚本缓存
通过EVAL命令执行Lua脚本的时候每次都需要将脚本的源代码包含在请求中。重复调用EVAL命令来执行同一脚本既浪费了网络带宽也在Redis中有一些开销。当然节省网络和计算资源是关键因此Redis为脚本提供了一种缓存机制。
默认情况下使用EVAL执行的每个脚本都存储在服务器的专用缓存中缓存的脚本内容通过脚本源代码的SHA1校验和来唯一标识 缓存在底层的server.lua_scripts 字典中。 我们可以通过先运行EVAL命令执行一个新的Lua脚本随后调用INFO memory命令来验证此缓存行为。我们会注意到used_memory_scripts_human和number_of_cached_scripts等指标会随着每一个新脚本的执行而增长。
即使是通过EVAL执行相同的脚本也会花费额外的开销例如词法分析、语法分析等等。
对于这个问题Redis提供了一个优化措施。
我们可以先调用SCRIPT LOAD script加载Lua脚本, 该命令会返回给客户端一个跟该Lua脚本向关联的SHA1校验和, 该命令并不会执行脚本而是编译脚本并将其加载到服务器的缓存中。
后续调用该Lua脚本的时候, 只需通过EVALSHA SHA1 numkeys key [key …] arg [arg …]命令, 将SHA1当做参数进行传递使用从服务器返回的SHA1摘要执行缓存的脚本即可。
下面是一个加载并执行缓存脚本的例子
SCRIPT LOAD return cache script
7d55b8f344d4d16a2d72bf255c993b3640f730adEVALSHA 7d55b8f344d4d16a2d72bf255c993b3640f730ad 0
cache script脚本缓存稳定性
默认情况下Redis将会一直在内存中缓存执行过的Lua脚本。但是Eval脚本被视为客户端应用程序的一部分Lua脚本的缓存并不会在Redis服务端进行持久化也就是说Redis脚本缓存总是不稳定的。
因为缓存脚本没有提供持久化能力其可以在服务器重新启动时清除也可以在副本承担主角色时的故障转移期间清除或者通过SCRIPT FLUSH命令显式清除。
在使用EVALSHA命令时若SHA1值对应的脚本未缓存至Redis中Redis会返回NOSCRIPT错误此时我们需要通过EVAL或SCRIPT LOAD命令将目标脚本缓存至Redis中后进行重试。
EVALSHA ffffffffffffffffffffffffffffffffffffffff 0
NOSCRIPT No matching script. Please use EVAL.实际上通过redisTemplate#execute()方法执行的Lua脚本其内部就是使用的此逻辑首先计算出Lua脚本的SHA1校验和后续调用时直接通过EVALSHA命令执行。因此对于方法参数DefaultRedisScript也是推荐单例实现的即不必每次调用该方法创建一个新的DefaultRedisScript。
脚本参数化
基于脚本缓存的考虑在应用程序运行时不断的动态生成脚本并且通过Eval发送给Redis执行很可能会耗尽主机用于缓存脚本的内存资源动态生成脚本是一种反模式。
因此脚本应该尽可能通用也就是参数化将共同的部分作为一个脚本源代码对于动态变更的数据尽量作为参数传递而不是每次动态生成一个新的脚本发给Redis执行。
这样的好处除了是可以节省脚本缓存资源还可以节省网络带宽开销和计算开销。 例如下面两个命令就是让应用程序根据需要动态生成不同的脚本源代码 EVAL return Hello 0
Hello EVAL return Scripting! 0
Scripting!但这样会让Redis缓存两个Lua脚本我们对其进行参数化变化的部分数据通过参数传递如下
SCRIPT LOAD return ARGV[1]
098e0f0d1448c0a81dafe820f66d460eb09263daEVALSHA 098e0f0d1448c0a81dafe820f66d460eb09263da 0 Hello
HelloEVALSHA 098e0f0d1448c0a81dafe820f66d460eb09263da 0 Parameterization!
Parameterization!这样Redis就只需要在第一次调用的时候缓存改脚本并获取SHA1 校验和对于后续的执行只需要使用EVALSHA命令输入脚本的 SHA1 校验和以及脚本参数就可以调用之前存储的脚本。
脚本超时处理
Lua脚本在Redis中和普通命令一样是原子执行的Lua脚本执行过慢将会导致Redis阻塞将会影响所有连接的客户端。
脚本还有一个最大执行时间限制它的默认值是5s可以通过编辑redis.conf 文件或者使用 CONFIG GET parameter 和 CONFIG SET parameter value 命令来修改 lua-time-limit 选项来控制单位ms。
执行时间超过了最大执行时间的Lua脚本被称为慢脚本。
即使一个脚本执行超时因为 Redis 必须保证脚本执行的原子性所以它并不会自动停止。
因此当脚本运行的时间超过最大执行时间后以下动作会被执行
Redis 开始重新接受其他客户端的命令请求但是只有SCRIPT KILL 和 SHUTDOWN NOSAVE两个命令会被处理对于其他命令请求 Redis 服务器只是简单地返回 BUSY 错误BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.SCRIPT KILL命令只能用于在执行期间没有修改数据集的脚本因为停止只读脚本并不违反脚本引擎的保证原子性对于有写入数据的脚本无法停止。如果脚本已经执行过写命令那么唯一允许执行的操作就是SHUTDOWN NOSAVE 它通过停止服务器来阻止当前数据集写入磁盘。
超时处理简单的原理是
如果配置了lua-time-limit 选项那么Reids执行Lua脚本之前会在Lua环境里面设置一个超时处理钩子hook超时处理钩子在脚本运行期间会定期检查脚本已经运行了多长时间。
如果超过了最大执行时间那么超时钩子将定期查看是否有SCRIPT KILL命令或者SHUTDOWN NOSAVE 命令到达服务器然后执行后续判断。
Redis集群中Lua脚本的使用
一般的Lua脚本在Redis单节点模式以及主从哨兵模式下都能正常使用。
如果是Redis Cluster集群模式下每个node节点会分配到不同的哈希槽hash slot默认16384个存储数据的时候会根据CRC16算法计算出key的哈希值然后对16384取余计算出该key对应的slot从而实现数据分片。
如果使用Redis集群一次性对多个key执行操作例如mset,mget命令包括对Lua脚本中的多个key执行调用时要求所有操作的key必须在同一个节点上否则会执行出错。
因此Redis 集群对使用Lua脚本的使用增加了额外的限制
为了防止key不再同一个nodeRedis在执行某个Lua脚本之前会对KEYS中的所有key进行判断如果所有的key不是位于同一个slot上那么报错xxxx command keys must be in same slot。注意即使key所属的不同slot位于同一个node也还是会报错。我们可以通过CLUSTER KEYSLOT命令获取目标Key的哈希槽hash slot。通过EVAL或者SCRIPT LOAD命令缓存的脚本不会同步到其他节点中。
更好的办法是让需要Lua脚本操作的key都位于同一个slothashTag是Redis集群支持的一个特性是一种确保在同一个slot中分配多个key的方法目的是为了让Redis集群支持多key操作。
为key设置hashTag的方式很简单我们只需要在key中追加{xxx}字符串即可Redis会取key中第一个遇到的{}中的字符串来进行slot计算。详细逻辑见https://redis.io/docs/reference/cluster-spec/#hash-tags
例如原来的key为key1和key2如果按照正常计算方式他们可能不会位于同一个slot为他们分配相同的hashTag之后变成{test}key1 {test}key2。此时集群会根据hashTag决定key分配到的slot因为他们的hashTag都是test因此它们会被分配到同一个slot。
例如集群模式下执行如下命令
EVAL redis.call(mset,KEYS[1],ARGV[1],KEYS[2],ARGV[2]) 2 key1 key2 value1 value2将会收到报错 ERR ‘key1’ and ‘key2’ not in the same slot。
我们采用hashTag改写key之后将会正常执行
EVAL redis.call(mset,KEYS[1],ARGV[1],KEYS[2],ARGV[2]) 2 {test}key1 {test}key2 value1 value2将会执行成功
其他限制
Redis包含一个嵌入式Lua 5.1解释器。解释器运行用户定义的临时脚本和函数。脚本在一个沙箱上下文中运行只能访问特定的Lua包https://redis.io/docs/manual/programmability/lua-api/。
不要访问系统环境 Lua脚本脚本永远不应该尝试访问Redis服务器的底层主机系统。这包括文件系统、网络和执行系统调用的任何其他尝试而不是API所支持的那些尝试。我们的Lua脚本应该只对存储在Redis中的数据进行操作。
无法定义全局变量和函数 所有变量和函数定义都必须声明为局部的。为此需要在声明前加上local关键字。默认支持的全局变量有redis对象、KEYS、ARGV
只通过KEYS传递key 为了确保在独立部署和集群部署环境中正确执行脚本上面讲到的Redis集群环境执行Lua脚本的问题脚本访问的所有Redis的key的必须显式通过KEYS参数传递。
Redis 7 Functions
如果有条件那么使用Lua的更好的方式是使用Redis 7 Functions机制Reids Funtions是从 7.0版开始提供的另一种可编程的方法可以看是持久化的Lua函数类似于关系型数据库的函数。
Redis Funtions支持在Redis服务端定义函数其好处如下
在服务端维护支持不同的客户端远程调用共同的函数Lua脚本则需要维护在各个客户端中。一旦Lua脚本更新那么所有的客户端都需要更改。支持函数的嵌套调用Lua脚本不能在一个脚本中调用另一个脚本。另外还支持函数的持久化Lua脚本则不支持服务端持久化。
关于更多的Redis 7 Functions的信息后续有机会再介绍。
Lua脚本相关常用命令
EVALEVAL script numkeys [key [key …]] [arg [arg …]]执行给定的脚本和参数并返回结果。参数说明scriptLua脚本。numkeys指定KEYS[]参数的数量非负整数。KEYS[]传入的Redis键参数。ARGV[]传入的脚本参数。KEYS[]与ARGV[]的索引均从1开始。EVALSHAEVALSHA sha1 numkeys key [key …] arg [arg …]给定脚本的SHA1校验和Redis将再次执行脚本。使用EVALSHA命令时若sha1值对应的脚本未缓存至Redis中Redis会返回NOSCRIPT错误请通过EVAL或SCRIPT LOAD命令将目标脚本缓存至Redis中后进行重试。SCRIPT LOADSCRIPT LOAD script将给定的script脚本缓存在Redis中并返回该脚本的SHA1校验和。该命令并不会执行脚本。SCRIPT EXISTSSCRIPT EXISTS script [script …]给定一个或多个脚本的SHA1返回每个SHA1对应的脚本是否已缓存在当前Redis服务。脚本已存在则返回1不存在则返回0。SCRIPT KILLSCRIPT KILL停止正在运行的Lua脚本。该命令是中断长时间运行的脚本(也就是慢脚本)的唯一方法除非关闭服务器。一旦脚本的执行持续时间超过了配置的最大执行时间阈值默认5s脚本就被视为慢脚本。SCRIPT KILL命令只能用于在执行期间没有修改数据集的脚本(因为停止只读脚本并不违反脚本引擎的保证原子性)对于有写入数据的脚本无法停止。SCRIPT FLUSHSCRIPT FLUSH清空当前Redis服务器中的所有Lua脚本缓存。该命令是强制Redis刷新脚本缓存的唯一方法。
总结
本次我们介绍了Redis Lua脚本的入门概念使用方式以及一些核心知识点当然还有一些知识点没有详细讲解比如Redis 7 函数、Lua脚本的复制等等。
更多的内容在Redis官网都有详细介绍下来后可以相互交流讨论。
参考
https://redis.io/topics/cluster-spec/ https://redis.io/commands/eval/ https://redis.io/docs/manual/programmability/ https://redis.io/docs/manual/programmability/lua-api/ 文章转载自: http://www.morning.qmnhw.cn.gov.cn.qmnhw.cn http://www.morning.hkpyp.cn.gov.cn.hkpyp.cn http://www.morning.xpzrx.cn.gov.cn.xpzrx.cn http://www.morning.pkrtz.cn.gov.cn.pkrtz.cn http://www.morning.gtcym.cn.gov.cn.gtcym.cn http://www.morning.zgdnd.cn.gov.cn.zgdnd.cn http://www.morning.sacxbs.cn.gov.cn.sacxbs.cn http://www.morning.snmsq.cn.gov.cn.snmsq.cn http://www.morning.gnbtp.cn.gov.cn.gnbtp.cn http://www.morning.bwttj.cn.gov.cn.bwttj.cn http://www.morning.bpmtl.cn.gov.cn.bpmtl.cn http://www.morning.bnzjx.cn.gov.cn.bnzjx.cn http://www.morning.slmbg.cn.gov.cn.slmbg.cn http://www.morning.bsrqy.cn.gov.cn.bsrqy.cn http://www.morning.mqffm.cn.gov.cn.mqffm.cn http://www.morning.touziyou.cn.gov.cn.touziyou.cn http://www.morning.rwxnn.cn.gov.cn.rwxnn.cn http://www.morning.trkhx.cn.gov.cn.trkhx.cn http://www.morning.fkgqn.cn.gov.cn.fkgqn.cn http://www.morning.qxlyf.cn.gov.cn.qxlyf.cn http://www.morning.zdhxm.com.gov.cn.zdhxm.com http://www.morning.ntwxt.cn.gov.cn.ntwxt.cn http://www.morning.zcfmb.cn.gov.cn.zcfmb.cn http://www.morning.xfjwm.cn.gov.cn.xfjwm.cn http://www.morning.gglhj.cn.gov.cn.gglhj.cn http://www.morning.chongzhanggui.cn.gov.cn.chongzhanggui.cn http://www.morning.hlzpb.cn.gov.cn.hlzpb.cn http://www.morning.sqyjh.cn.gov.cn.sqyjh.cn http://www.morning.xylxm.cn.gov.cn.xylxm.cn http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.qxrct.cn.gov.cn.qxrct.cn http://www.morning.gdljq.cn.gov.cn.gdljq.cn http://www.morning.lqljj.cn.gov.cn.lqljj.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.tsyny.cn.gov.cn.tsyny.cn http://www.morning.rqgq.cn.gov.cn.rqgq.cn http://www.morning.rpwm.cn.gov.cn.rpwm.cn http://www.morning.xlbyx.cn.gov.cn.xlbyx.cn http://www.morning.qhczg.cn.gov.cn.qhczg.cn http://www.morning.wtnwf.cn.gov.cn.wtnwf.cn http://www.morning.qkgwx.cn.gov.cn.qkgwx.cn http://www.morning.tcxzn.cn.gov.cn.tcxzn.cn http://www.morning.slfmp.cn.gov.cn.slfmp.cn http://www.morning.sfcfy.cn.gov.cn.sfcfy.cn http://www.morning.btgxf.cn.gov.cn.btgxf.cn http://www.morning.cttti.com.gov.cn.cttti.com http://www.morning.gtqws.cn.gov.cn.gtqws.cn http://www.morning.dmthy.cn.gov.cn.dmthy.cn http://www.morning.pmhln.cn.gov.cn.pmhln.cn http://www.morning.fqcdh.cn.gov.cn.fqcdh.cn http://www.morning.mkxxk.cn.gov.cn.mkxxk.cn http://www.morning.zhnyj.cn.gov.cn.zhnyj.cn http://www.morning.dydqh.cn.gov.cn.dydqh.cn http://www.morning.fsbns.cn.gov.cn.fsbns.cn http://www.morning.cmqrg.cn.gov.cn.cmqrg.cn http://www.morning.skdrp.cn.gov.cn.skdrp.cn http://www.morning.jcffp.cn.gov.cn.jcffp.cn http://www.morning.zpqlf.cn.gov.cn.zpqlf.cn http://www.morning.bpzw.cn.gov.cn.bpzw.cn http://www.morning.lwqst.cn.gov.cn.lwqst.cn http://www.morning.rrdch.cn.gov.cn.rrdch.cn http://www.morning.rblqk.cn.gov.cn.rblqk.cn http://www.morning.fflnw.cn.gov.cn.fflnw.cn http://www.morning.zczkm.cn.gov.cn.zczkm.cn http://www.morning.jrwbl.cn.gov.cn.jrwbl.cn http://www.morning.mjkqj.cn.gov.cn.mjkqj.cn http://www.morning.bpmfn.cn.gov.cn.bpmfn.cn http://www.morning.bmsqq.cn.gov.cn.bmsqq.cn http://www.morning.tbwsl.cn.gov.cn.tbwsl.cn http://www.morning.yqhdy.cn.gov.cn.yqhdy.cn http://www.morning.yhxhq.cn.gov.cn.yhxhq.cn http://www.morning.ltypx.cn.gov.cn.ltypx.cn http://www.morning.zpkfb.cn.gov.cn.zpkfb.cn http://www.morning.rcttz.cn.gov.cn.rcttz.cn http://www.morning.kjnfs.cn.gov.cn.kjnfs.cn http://www.morning.tfznk.cn.gov.cn.tfznk.cn http://www.morning.ndcf.cn.gov.cn.ndcf.cn http://www.morning.tkyry.cn.gov.cn.tkyry.cn http://www.morning.qpljg.cn.gov.cn.qpljg.cn http://www.morning.c7496.cn.gov.cn.c7496.cn