景点购票网站开发,从百万到千万 网站怎么优化,合肥建设网站查询,海口模板建站定制网站多级缓存的实现离不开Nginx编程#xff0c;而Nginx编程又离不开OpenResty。
1. OpenResty快速入门
我们希望达到的多级缓存架构如图#xff1a; 其中#xff1a; windows上的nginx用来做反向代理服务#xff0c;将前端的查询商品的ajax请求代理到OpenResty集群 OpenRest…多级缓存的实现离不开Nginx编程而Nginx编程又离不开OpenResty。
1. OpenResty快速入门
我们希望达到的多级缓存架构如图 其中 windows上的nginx用来做反向代理服务将前端的查询商品的ajax请求代理到OpenResty集群 OpenResty集群用来编写多级缓存业务
1.1. 反向代理流程
现在商品详情页使用的是假的商品数据。不过在浏览器中可以看到页面有发起ajax请求查询真实商品数据。
这个请求如下 请求地址是localhost端口是80就被windows上安装的Nginx服务给接收到了。然后代理给了OpenResty集群 我们需要在OpenResty中编写业务查询商品数据并返回到浏览器。
但是这次我们先在OpenResty接收请求返回假的商品数据。
1.2. OpenResty监听请求
OpenResty的很多功能都依赖于其目录下的Lua库需要在nginx.conf中指定依赖库的目录并导入依赖
1添加对OpenResty的Lua模块的加载
修改/usr/local/openresty/nginx/conf/nginx.conf文件在其中的http下面添加下面代码
#lua 模块
lua_package_path /usr/local/openresty/lualib/?.lua;;;
#c模块
lua_package_cpath /usr/local/openresty/lualib/?.so;;; 2监听/api/item路径
修改/usr/local/openresty/nginx/conf/nginx.conf文件在nginx.conf的server下面添加对/api/item这个路径的监听
location /api/item {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件来决定content_by_lua_file lua/item.lua;
}这个监听就类似于SpringMVC中的GetMapping(/api/item)做路径映射。
而content_by_lua_file lua/item.lua则相当于调用item.lua这个文件执行其中的业务把结果返回给用户。相当于java中调用service。
1.3. 编写item.lua
1在/usr/loca/openresty/nginx目录创建文件夹lua 2在/usr/loca/openresty/nginx/lua文件夹下新建文件item.lua 3编写item.lua返回假数据
item.lua中利用ngx.say()函数返回数据到Response中
ngx.say({id:10001,name:SALSA AIR,title:RIMOWA 21寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4,price:17900,image:https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp,category:拉杆箱,brand:RIMOWA,spec:,status:1,createTime:2019-04-30T16:00:00.00000:00,updateTime:2019-04-30T16:00:00.00000:00,stock:2999,sold:31290})4重新加载配置
nginx -s reload刷新商品页面http://localhost/item.html?id1001即可看到效果 2. 请求参数处理
我们在OpenResty接收前端请求但是返回的是假数据。
要返回真实数据必须根据前端传递来的商品id查询商品信息才可以。
那么如何获取前端传递的商品参数呢
2.1. 获取参数的API
OpenResty中提供了一些API用来获取不同类型的前端请求参数 2.2. 获取参数并返回
在前端发起的ajax请求如图 可以看到商品id是以路径占位符方式传递的因此可以利用正则表达式匹配的方式来获取ID
1获取商品id
修改/usr/loca/openresty/nginx/nginx.conf文件中监听/api/item的代码利用正则表达式获取ID
location ~ /api/item/(\d) {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件来决定content_by_lua_file lua/item.lua;
}2拼接ID并返回
修改/usr/loca/openresty/nginx/lua/item.lua文件获取id并拼接到结果中返回
-- 获取商品id
local id ngx.var[1]
-- 拼接并返回
ngx.say({id: .. id .. ,name:SALSA AIR,title:RIMOWA 21寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4,price:17900,image:https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp,category:拉杆箱,brand:RIMOWA,spec:,status:1,createTime:2019-04-30T16:00:00.00000:00,updateTime:2019-04-30T16:00:00.00000:00,stock:2999,sold:31290})3重新加载并测试
运行命令以重新加载OpenResty配置
nginx -s reload刷新页面可以看到结果中已经带上了ID 3. 查询Tomcat
拿到商品ID后本应去缓存中查询商品信息不过目前我们还未建立nginx、redis缓存。因此这里我们先根据商品id去tomcat查询商品信息。我们实现如图部分 需要注意的是我们的OpenResty是在虚拟机Tomcat是在Windows电脑上。两者IP一定不要搞错了。 3.1. 发送http请求的API
nginx提供了内部API用以发送http请求
local resp ngx.location.capture(/path,{method ngx.HTTP_GET, -- 请求方式args {a1,b2}, -- get方式传参数
})返回的响应内容包括
resp.status响应状态码resp.header响应头是一个tableresp.body响应体就是响应数据
注意这里的path是路径并不包含IP和端口。这个请求会被nginx内部的server监听并处理。
但是我们希望这个请求发送到Tomcat服务器所以还需要编写一个server来对这个路径做反向代理 location /path {# 这里是windows电脑的ip和Java服务端口需要确保windows防火墙处于关闭状态proxy_pass http://192.168.150.1:8081; }原理如图 3.2. 封装http工具
下面我们封装一个发送Http请求的工具基于ngx.location.capture来实现查询tomcat。
1添加反向代理到windows的Java服务
因为item-service中的接口都是/item开头所以我们监听/item路径代理到windows上的tomcat服务。
修改 /usr/local/openresty/nginx/conf/nginx.conf文件添加一个location
location /item {proxy_pass http://192.168.150.1:8081;
}以后只要我们调用ngx.location.capture(/item)就一定能发送请求到windows的tomcat服务。
2封装工具类
之前我们说过OpenResty启动时会加载以下两个目录中的工具文件 所以自定义的http工具也需要放到这个目录下。
在/usr/local/openresty/lualib目录下新建一个common.lua文件
vi /usr/local/openresty/lualib/common.lua内容如下:
-- 封装函数发送http请求并解析响应
local function read_http(path, params)local resp ngx.location.capture(path,{method ngx.HTTP_GET,args params,})if not resp then-- 记录错误信息返回404ngx.log(ngx.ERR, http请求查询失败, path: , path , , args: , args)ngx.exit(404)endreturn resp.body
end
-- 将方法导出
local _M { read_http read_http
}
return _M这个工具将read_http函数封装到_M这个table类型的变量中并且返回这类似于导出。
使用的时候可以利用require(common)来导入该函数库这里的common是函数库的文件名。
3实现商品查询
最后我们修改/usr/local/openresty/lua/item.lua文件利用刚刚封装的函数库实现对tomcat的查询
-- 引入自定义common工具模块返回值是common中返回的 _M
local common require(common)
-- 从 common中获取read_http这个函数
local read_http common.read_http
-- 获取路径参数
local id ngx.var[1]
-- 根据id查询商品
local itemJSON read_http(/item/.. id, nil)
-- 根据id查询商品库存
local itemStockJSON read_http(/item/stock/.. id, nil)这里查询到的结果是json字符串并且包含商品、库存两个json字符串页面最终需要的是把两个json拼接为一个json 这就需要我们先把JSON变为lua的table完成数据整合后再转为JSON。
3.3. CJSON工具类
OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。
官方地址 https://github.com/openresty/lua-cjson/
1引入cjson模块
local cjson require cjson2序列化
local obj {name jack,age 21
}
-- 把 table 序列化为 json
local json cjson.encode(obj)3反序列化
local json {name: jack, age: 21}
-- 反序列化 json为 table
local obj cjson.decode(json);
print(obj.name)3.4. 实现Tomcat查询
下面我们修改之前的item.lua中的业务添加json处理功能
-- 导入common函数库
local common require(common)
local read_http common.read_http
-- 导入cjson库
local cjson require(cjson)-- 获取路径参数
local id ngx.var[1]
-- 根据id查询商品
local itemJSON read_http(/item/.. id, nil)
-- 根据id查询商品库存
local itemStockJSON read_http(/item/stock/.. id, nil)-- JSON转化为lua的table
local item cjson.decode(itemJSON)
local stock cjson.decode(stockJSON)-- 组合数据
item.stock stock.stock
item.sold stock.sold-- 把item序列化为json 返回结果
ngx.say(cjson.encode(item))3.5. 基于ID负载均衡
刚才的代码中我们的tomcat是单机部署。而实际开发中tomcat一定是集群模式 因此OpenResty需要对tomcat集群做负载均衡。
而默认的负载均衡规则是轮询模式当我们查询/item/10001时
第一次会访问8081端口的tomcat服务在该服务内部就形成了JVM进程缓存第二次会访问8082端口的tomcat服务该服务内部没有JVM缓存因为JVM缓存无法共享会查询数据库…
你看因为轮询的原因第一次查询8081形成的JVM缓存并未生效直到下一次再次访问到8081时才可以生效缓存命中率太低了。
怎么办
如果能让同一个商品每次查询时都访问同一个tomcat服务那么JVM缓存就一定能生效了。
也就是说我们需要根据商品id做负载均衡而不是轮询。
1原理
nginx提供了基于请求路径做负载均衡的算法
nginx根据请求路径做hash运算把得到的数值对tomcat服务的数量取余余数是几就访问第几个服务实现负载均衡。
例如
我们的请求路径是 /item/10001tomcat总数为2台8081、8082对请求路径/item/1001做hash运算求余的结果为1则访问第一个tomcat服务也就是8081
只要id不变每次hash运算结果也不会变那就可以保证同一个商品一直访问同一个tomcat服务确保JVM缓存生效。
2实现
修改/usr/local/openresty/nginx/conf/nginx.conf文件实现基于ID做负载均衡。
首先定义tomcat集群并设置基于路径做负载均衡
upstream tomcat-cluster {hash $request_uri;server 192.168.150.1:8081;server 192.168.150.1:8082;
}然后修改对tomcat服务的反向代理目标指向tomcat集群
location /item {proxy_pass http://tomcat-cluster;
}重新加载OpenResty
nginx -s reload3测试
启动两台tomcat服务 同时启动 清空日志后再次访问页面可以看到不同id的商品访问到了不同的tomcat服务 4. Redis缓存预热
Redis缓存会面临冷启动问题
冷启动服务刚刚启动时Redis中并没有缓存如果所有商品数据都在第一次查询时添加缓存可能会给数据库带来较大压力。
缓存预热在实际开发中我们可以利用大数据统计用户访问的热点数据在项目启动时将这些热点数据提前查询并保存到Redis中。
我们数据量较少并且没有数据统计相关功能目前可以在启动时将所有数据都放入缓存中。
1利用Docker安装Redis
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes2在item-service服务中引入Redis依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency3配置Redis地址
spring:redis:host: 192.168.150.1014编写初始化类
缓存预热需要在项目启动时完成并且必须是拿到RedisTemplate之后。
这里我们利用InitializingBean接口来实现因为InitializingBean可以在对象被Spring创建并且成员变量全部注入后执行。
package com.dcxuexi.item.config;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.dcxuexi.item.pojo.Item;
import com.dcxuexi.item.pojo.ItemStock;
import com.dcxuexi.item.service.IItemService;
import com.dcxuexi.item.service.IItemStockService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.List;Component
public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;private static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {// 初始化缓存// 1.查询商品信息ListItem itemList itemService.list();// 2.放入缓存for (Item item : itemList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(item);// 2.2.存入redisredisTemplate.opsForValue().set(item:id: item.getId(), json);}// 3.查询商品库存信息ListItemStock stockList stockService.list();// 4.放入缓存for (ItemStock stock : stockList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(stock);// 2.2.存入redisredisTemplate.opsForValue().set(item:stock:id: stock.getId(), json);}}
}5. 查询Redis缓存
现在Redis缓存已经准备就绪我们可以再OpenResty中实现查询Redis的逻辑了。如下图红框所示 当请求进入OpenResty之后
优先查询Redis缓存如果Redis缓存未命中再查询Tomcat
5.1. 封装Redis工具
OpenResty提供了操作Redis的模块我们只要引入该模块就能直接使用。但是为了方便我们将Redis操作封装到之前的common.lua工具库中。
修改/usr/local/openresty/lualib/common.lua文件
1引入Redis模块并初始化Redis对象
-- 导入redis
local redis require(resty.redis)
-- 初始化redis
local red redis:new()
red:set_timeouts(1000, 1000, 1000)2封装函数用来释放Redis连接其实是放入连接池
-- 关闭redis连接的工具方法其实是放入连接池
local function close_redis(red)local pool_max_idle_time 10000 -- 连接的空闲时间单位是毫秒local pool_size 100 --连接池大小local ok, err red:set_keepalive(pool_max_idle_time, pool_size)if not ok thenngx.log(ngx.ERR, 放入redis连接池失败: , err)end
end3封装函数根据key查询Redis数据
-- 查询redis的方法 ip和port是redis地址key是查询的key
local function read_redis(ip, port, key)-- 获取一个连接local ok, err red:connect(ip, port)if not ok thenngx.log(ngx.ERR, 连接redis失败 : , err)return nilend-- 查询redislocal resp, err red:get(key)-- 查询失败处理if not resp thenngx.log(ngx.ERR, 查询Redis失败: , err, , key , key)end--得到的数据为空处理if resp ngx.null thenresp nilngx.log(ngx.ERR, 查询Redis数据为空, key , key)endclose_redis(red)return resp
end4导出
-- 将方法导出
local _M { read_http read_http,read_redis read_redis
}
return _M完整的common.lua
-- 导入redis
local redis require(resty.redis)
-- 初始化redis
local red redis:new()
red:set_timeouts(1000, 1000, 1000)-- 关闭redis连接的工具方法其实是放入连接池
local function close_redis(red)local pool_max_idle_time 10000 -- 连接的空闲时间单位是毫秒local pool_size 100 --连接池大小local ok, err red:set_keepalive(pool_max_idle_time, pool_size)if not ok thenngx.log(ngx.ERR, 放入redis连接池失败: , err)end
end-- 查询redis的方法 ip和port是redis地址key是查询的key
local function read_redis(ip, port, key)-- 获取一个连接local ok, err red:connect(ip, port)if not ok thenngx.log(ngx.ERR, 连接redis失败 : , err)return nilend-- 查询redislocal resp, err red:get(key)-- 查询失败处理if not resp thenngx.log(ngx.ERR, 查询Redis失败: , err, , key , key)end--得到的数据为空处理if resp ngx.null thenresp nilngx.log(ngx.ERR, 查询Redis数据为空, key , key)endclose_redis(red)return resp
end-- 封装函数发送http请求并解析响应
local function read_http(path, params)local resp ngx.location.capture(path,{method ngx.HTTP_GET,args params,})if not resp then-- 记录错误信息返回404ngx.log(ngx.ERR, http查询失败, path: , path , , args: , args)ngx.exit(404)endreturn resp.body
end
-- 将方法导出
local _M { read_http read_http,read_redis read_redis
}
return _M5.2. 实现Redis查询
接下来我们就可以去修改item.lua文件实现对Redis的查询了。
查询逻辑是
根据id查询Redis如果查询失败则继续查询Tomcat将查询结果返回
1修改/usr/local/openresty/lua/item.lua文件添加一个查询函数
-- 导入common函数库
local common require(common)
local read_http common.read_http
local read_redis common.read_redis
-- 封装查询函数
function read_data(key, path, params)-- 查询本地缓存local val read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询http key: , key)-- redis查询失败去查询httpval read_http(path, params)end-- 返回数据return val
end2而后修改商品查询、库存查询的业务 3完整的item.lua代码
-- 导入common函数库
local common require(common)
local read_http common.read_http
local read_redis common.read_redis
-- 导入cjson库
local cjson require(cjson)-- 封装查询函数
function read_data(key, path, params)-- 查询本地缓存local val read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询http key: , key)-- redis查询失败去查询httpval read_http(path, params)end-- 返回数据return val
end-- 获取路径参数
local id ngx.var[1]-- 查询商品信息
local itemJSON read_data(item:id: .. id, /item/ .. id, nil)
-- 查询库存信息
local stockJSON read_data(item:stock:id: .. id, /item/stock/ .. id, nil)-- JSON转化为lua的table
local item cjson.decode(itemJSON)
local stock cjson.decode(stockJSON)
-- 组合数据
item.stock stock.stock
item.sold stock.sold-- 把item序列化为json 返回结果
ngx.say(cjson.encode(item))6. Nginx本地缓存
现在整个多级缓存中只差最后一环也就是nginx的本地缓存了。如图
6.1. 本地缓存API
OpenResty为Nginx提供了 shard dict 的功能可以在nginx的多个worker之间共享数据实现缓存功能。
1开启共享字典在nginx.conf的http下添加配置 # 共享字典也就是本地缓存名称叫做item_cache大小150mlua_shared_dict item_cache 150m; 2操作共享字典
-- 获取本地缓存对象
local item_cache ngx.shared.item_cache
-- 存储, 指定key、value、过期时间单位s默认为0代表永不过期
item_cache:set(key, value, 1000)
-- 读取
local val item_cache:get(key)6.2. 实现本地缓存查询
1修改/usr/local/openresty/lua/item.lua文件修改read_data查询函数添加本地缓存逻辑
-- 导入共享词典本地缓存
local item_cache ngx.shared.item_cache-- 封装查询函数
function read_data(key, expire, path, params)-- 查询本地缓存local val item_cache:get(key)if not val thenngx.log(ngx.ERR, 本地缓存查询失败尝试查询Redis key: , key)-- 查询redisval read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询http key: , key)-- redis查询失败去查询httpval read_http(path, params)endend-- 查询成功把数据写入本地缓存item_cache:set(key, val, expire)-- 返回数据return val
end2修改item.lua中查询商品和库存的业务实现最新的read_data函数 其实就是多了缓存时间参数过期后nginx缓存会自动删除下次访问即可更新缓存。
这里给商品基本信息设置超时时间为30分钟库存为1分钟。
因为库存更新频率较高如果缓存时间过长可能与数据库差异较大。
3完整的item.lua文件
-- 导入common函数库
local common require(common)
local read_http common.read_http
local read_redis common.read_redis
-- 导入cjson库
local cjson require(cjson)
-- 导入共享词典本地缓存
local item_cache ngx.shared.item_cache-- 封装查询函数
function read_data(key, expire, path, params)-- 查询本地缓存local val item_cache:get(key)if not val thenngx.log(ngx.ERR, 本地缓存查询失败尝试查询Redis key: , key)-- 查询redisval read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询http key: , key)-- redis查询失败去查询httpval read_http(path, params)endend-- 查询成功把数据写入本地缓存item_cache:set(key, val, expire)-- 返回数据return val
end-- 获取路径参数
local id ngx.var[1]-- 查询商品信息
local itemJSON read_data(item:id: .. id, 1800, /item/ .. id, nil)
-- 查询库存信息
local stockJSON read_data(item:stock:id: .. id, 60, /item/stock/ .. id, nil)-- JSON转化为lua的table
local item cjson.decode(itemJSON)
local stock cjson.decode(stockJSON)
-- 组合数据
item.stock stock.stock
item.sold stock.sold-- 把item序列化为json 返回结果
ngx.say(cjson.encode(item))
文章转载自: http://www.morning.zthln.cn.gov.cn.zthln.cn http://www.morning.fhykt.cn.gov.cn.fhykt.cn http://www.morning.rgsgk.cn.gov.cn.rgsgk.cn http://www.morning.fprll.cn.gov.cn.fprll.cn http://www.morning.zqkr.cn.gov.cn.zqkr.cn http://www.morning.nxfwf.cn.gov.cn.nxfwf.cn http://www.morning.rpdmj.cn.gov.cn.rpdmj.cn http://www.morning.mdmqg.cn.gov.cn.mdmqg.cn http://www.morning.fndfn.cn.gov.cn.fndfn.cn http://www.morning.sfhjx.cn.gov.cn.sfhjx.cn http://www.morning.kfcz.cn.gov.cn.kfcz.cn http://www.morning.psyrz.cn.gov.cn.psyrz.cn http://www.morning.rwhlf.cn.gov.cn.rwhlf.cn http://www.morning.kyjyt.cn.gov.cn.kyjyt.cn http://www.morning.pdmml.cn.gov.cn.pdmml.cn http://www.morning.kgqpx.cn.gov.cn.kgqpx.cn http://www.morning.drpbc.cn.gov.cn.drpbc.cn http://www.morning.homayy.com.gov.cn.homayy.com http://www.morning.rhpgk.cn.gov.cn.rhpgk.cn http://www.morning.swwpl.cn.gov.cn.swwpl.cn http://www.morning.xrrjb.cn.gov.cn.xrrjb.cn http://www.morning.ckwrn.cn.gov.cn.ckwrn.cn http://www.morning.brnwc.cn.gov.cn.brnwc.cn http://www.morning.yaqi6.com.gov.cn.yaqi6.com http://www.morning.xlxmy.cn.gov.cn.xlxmy.cn http://www.morning.fmkbk.cn.gov.cn.fmkbk.cn http://www.morning.gjssk.cn.gov.cn.gjssk.cn http://www.morning.xtlty.cn.gov.cn.xtlty.cn http://www.morning.ydryk.cn.gov.cn.ydryk.cn http://www.morning.fhcwm.cn.gov.cn.fhcwm.cn http://www.morning.tgyqq.cn.gov.cn.tgyqq.cn http://www.morning.nbmyg.cn.gov.cn.nbmyg.cn http://www.morning.tlbdy.cn.gov.cn.tlbdy.cn http://www.morning.gpnwq.cn.gov.cn.gpnwq.cn http://www.morning.pqyms.cn.gov.cn.pqyms.cn http://www.morning.ngcth.cn.gov.cn.ngcth.cn http://www.morning.mbzlg.cn.gov.cn.mbzlg.cn http://www.morning.qywfw.cn.gov.cn.qywfw.cn http://www.morning.mnsts.cn.gov.cn.mnsts.cn http://www.morning.btpll.cn.gov.cn.btpll.cn http://www.morning.qlwfz.cn.gov.cn.qlwfz.cn http://www.morning.mzjbz.cn.gov.cn.mzjbz.cn http://www.morning.fzqfb.cn.gov.cn.fzqfb.cn http://www.morning.msgcj.cn.gov.cn.msgcj.cn http://www.morning.plnry.cn.gov.cn.plnry.cn http://www.morning.bgygx.cn.gov.cn.bgygx.cn http://www.morning.wbxbj.cn.gov.cn.wbxbj.cn http://www.morning.drrt.cn.gov.cn.drrt.cn http://www.morning.bpptt.cn.gov.cn.bpptt.cn http://www.morning.qhczg.cn.gov.cn.qhczg.cn http://www.morning.qgwpx.cn.gov.cn.qgwpx.cn http://www.morning.wzwpz.cn.gov.cn.wzwpz.cn http://www.morning.pyncx.cn.gov.cn.pyncx.cn http://www.morning.pshpx.cn.gov.cn.pshpx.cn http://www.morning.xysdy.cn.gov.cn.xysdy.cn http://www.morning.lmmyl.cn.gov.cn.lmmyl.cn http://www.morning.qrlkt.cn.gov.cn.qrlkt.cn http://www.morning.yrjym.cn.gov.cn.yrjym.cn http://www.morning.txrq.cn.gov.cn.txrq.cn http://www.morning.jmspy.cn.gov.cn.jmspy.cn http://www.morning.rjnm.cn.gov.cn.rjnm.cn http://www.morning.rklgm.cn.gov.cn.rklgm.cn http://www.morning.wjhnx.cn.gov.cn.wjhnx.cn http://www.morning.lhhdy.cn.gov.cn.lhhdy.cn http://www.morning.hjwkq.cn.gov.cn.hjwkq.cn http://www.morning.plgbh.cn.gov.cn.plgbh.cn http://www.morning.zgztn.cn.gov.cn.zgztn.cn http://www.morning.hrpjx.cn.gov.cn.hrpjx.cn http://www.morning.dmwjl.cn.gov.cn.dmwjl.cn http://www.morning.srhqm.cn.gov.cn.srhqm.cn http://www.morning.rdmz.cn.gov.cn.rdmz.cn http://www.morning.yesidu.com.gov.cn.yesidu.com http://www.morning.qfcnp.cn.gov.cn.qfcnp.cn http://www.morning.zbnkt.cn.gov.cn.zbnkt.cn http://www.morning.trpq.cn.gov.cn.trpq.cn http://www.morning.lyldhg.cn.gov.cn.lyldhg.cn http://www.morning.wttzp.cn.gov.cn.wttzp.cn http://www.morning.mwmxs.cn.gov.cn.mwmxs.cn http://www.morning.ctqbc.cn.gov.cn.ctqbc.cn http://www.morning.txlnd.cn.gov.cn.txlnd.cn