当前位置: 首页 > news >正文

网站建设在哪个软件下做中山网站快照优化公司

网站建设在哪个软件下做,中山网站快照优化公司,网上商城平台开发,在深圳做网站Redis避坑指南#xff1a;为什么要有分布式锁#xff1f;作者#xff1a;京东保险 张江涛1、为什么要有分布式锁#xff1f;JUC提供的锁机制#xff0c;可以保证在同一个JVM进程中同一时刻只有一个线程执行操作逻辑#xff1b;多服务多节点的情况下#xff0c;就意味着有…Redis避坑指南为什么要有分布式锁作者京东保险 张江涛1、为什么要有分布式锁JUC提供的锁机制可以保证在同一个JVM进程中同一时刻只有一个线程执行操作逻辑多服务多节点的情况下就意味着有多个JVM进程要做到这样就需要有一个中间人分布式锁就是用来保证在同一时刻仅有一个JVM进程中的一个线程在执行操作逻辑换句话说JUC的锁和分布式锁都是一种保护系统资源的措施。尽可能将并发带来的不确定性转换为同步的确定性2、分布式锁特性五大特性 非常重要特性1互斥性。在任意时刻只有一个客户端能持有锁。特性2 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁也能保证后续其他客户端能加锁。特性3 解铃还须系铃人。加锁和解锁必须是同一个客户端线程客户端自己不能把别人加的锁给解了。特性4可重入性。同一个现线程已经获取到锁可再次获取到锁。特性5 具有容错性。只要大部分的分布式锁节点正常运行客户端就可以加锁和解锁。2-1 常见分布式锁的三种实现方式1. 数据库锁2. 基于ZooKeeper的分布式锁3. 基于Redis的分布式锁。2-2 本文我们主要聊 redis实现分布式锁一个 setnx 就行了value没意义还有人认为 incr 也可以再加个超时时间就行了3、分布式锁特性2之不会发生死锁很多线程去上锁谁锁成功谁就有权利执行操作逻辑其他线程要么直接走抢锁失败的逻辑要么自旋尝试抢锁• 比方说 A线程竞争到了锁开始执行操作逻辑代码逻辑演示中使用 Jedis客户端为例publicstaticvoiddoSomething() {// RedisLock是封装好的一个类RedisLock redisLock new RedisLock(jedis); // 创建jedis实例的代码省略不是重点try {redisLock.lock(); // 上锁// 处理业务System.out.println(Thread.currentThread().getName() 线程处理业务逻辑中...);Thread.sleep(2000);System.out.println(Thread.currentThread().getName() 线程处理业务逻辑完毕);redisLock.unlock(); // 释放锁} catch (Exception e) {e.printStackTrace();} }• 正常情况下A 线程执行完操作逻辑后应该将锁释放。如果说执行过程中抛出异常程序不再继续走正常的释放锁流程没有释放锁怎么办所以我们想到• 释放锁的流程一定要在 finally{} 块中执行当然上锁的流程一定要在 finally{} 对应的 try{} 块中否则 finally{} 就没用了如下publicstaticvoiddoSomething() {RedisLock redisLock new RedisLock(jedis); // 创建jedis实例的代码省略不是重点try {redisLock.lock(); // 上锁必须在 try{}中// 处理业务System.out.println(Thread.currentThread().getName() 线程处理业务逻辑中...);Thread.sleep(2000);System.out.println(Thread.currentThread().getName() 线程处理业务逻辑完毕);} catch (Exception e) {e.printStackTrace();} finally {redisLock.unlock(); // 在finally{} 中释放锁} }写法注意 redisLock.lock(); 上分布式锁必须在 try{}中。在JAVA多线程中 lock.lock(); 单机多线程加锁操作需要在try{}之前。3-1 redisLock.unlock() 放在 finally{} 块中就行了吗还需要设置超时时间如果在执行 try{} 中逻辑的时候程序出现了 System.exit(0); 或者 finally{} 中执行异常比方说连接不上 redis-server了或者还未执行到 finally{}的时候JVM进程挂掉了服务宕机这些情况都会导致没有成功释放锁别的线程一直拿不到锁怎么办如果我的系统因为一个节点影响别的节点也都无法正常提供服务了那我的系统也太弱了。所以我们想到必须要将风险降低可以给锁设置一个超时时间比方说 1秒即便发生了上边的情况那我的锁也会在 1秒之后自动释放其他线程就可以获取到锁接班干活了publicstatic final String lock_key zjt-lock;publicvoidlock() { while (!tryLock()) {try {Thread.sleep(50); // 在while中自旋如果说读者想设置一些自旋次数等待最大时长等自己去扩展不是此处的重点} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(线程 threadName 占锁成功★★★);}private boolean tryLock() {SetParams setParams new SetParams();setParams.ex(1); // 超时时间1ssetParams.nx(); // nxString response jedis.set(lock_key, , setParams); // 转换为redis命令就是set zjt-key ex 1 nxreturnOK.equals(response);}注意上锁的时候设置key和设置超时时间这两个操作要是原子性的要么都执行要么都不执行。Redis原生支持// http://redis.io/commands/set.htmlSETkeyvalue[EX seconds][PX milliseconds][NX|XX]不要在代码里边分两次调用set k v exipre k time3-2 锁的超时时间该怎么计算刚才假设的超时时间 1s是怎么计算的这个时间该设多少合适呢锁中的业务逻辑的执行时间一般是我们在测试环境进行多次测试然后在压测环境多轮压测之后比方说计算出平均的执行时间是 200ms锁的超时时间放大3-5倍比如这里我们设置为 1s为啥要放大因为如果锁的操作逻辑中有网络 IO操作线上的网络不会总一帆风顺我们要给网络抖动留有缓冲时间。另外如果你设置 10s果真发生了宕机那意味着这 10s中间你的这个分布式锁的服务全部节点都是不可用的这个和你的业务以及系统的可用性有挂钩要衡量要慎重后边3-13会再详细聊。那如果一个节点宕机之后可以通知 redis-server释放锁吗注意我是宕机不可控力断电了兄弟通知不了的。回头一想如果我是优雅停机呢我不是 kill -9也不是断电这样似乎可以去做一些编码去释放锁你可以参考下 JVM的钩子、Dubbo的优雅停机、或者 linux进程级通信技术来做这件事情。当然也可以手动停服务后手动删除掉 redis中的锁。4、分布式锁特性3:解铃还须系铃人如果说 A线程在执行操作逻辑的过程中别的线程直接进行了释放锁的操作是不是就出问题了什么别的线程没有获得锁却直接执行了释放锁现在是 A线程上的锁那肯定只能 A线程释放锁呀别的线程释放锁算怎么回事联想 ReentrantLock中的 isHeldByCurrentThread()方法所以我们想到必须在锁上加个标记只有上锁的线程 A线程知道相当于是一个密语也就是说释放锁的时候首先先把密语和锁上的标记进行匹配如果匹配不上就没有权利释放锁private boolean tryLock() {SetParams setParams new SetParams();setParams.ex(1); // 超时时间1ssetParams.nx(); // nxString response jedis.set(lock_key, , setParams); // 转换为redis命令就是set zjt_key ex 1 nxreturnOK.equals(response);}// 别的线程直接调用释放锁操作分布式锁崩溃publicvoidunlock() {jedis.del(encode(lock_key));System.out.println(线程 threadName 释放锁成功☆☆☆);}privatebyte[] encode(String param) {return param.getBytes();}4-1 这个密语value约定设置成什么呢很多同学说设置成一个 UUID就行了上锁之前在该线程代码中生成一个 UUID将这个作为秘钥存在锁键的 value中释放锁的时候用这个进行校验因为只有上锁的线程知道这个秘钥别的线程是不知道的。这个可行吗当然可行。String releaseLock_lua if redis.call(\get\,KEYS[1]) ARGV[1] \n then\n return redis.call(\del\, KEYS[1])\n else\n return 0\n end;privatebooleantryLock(String uuid) {SetParams setParams newSetParams();setParams.ex(1); // 超时时间1ssetParams.nx(); // nxString response jedis.set(lock_key, uuid, setParams); // 转换为redis命令就是set zjt-key ex 1 nxreturnOK.equals(response);}publicvoidunlock(String uuid) {Listbyte[] keys Arrays.asList(encode(lock_key));Listbyte[] args Arrays.asList(encode(uuid));// 使用lua脚本保证原子性long eval (Long) jedis.eval(encode(releaseLock_lua), keys, args);if (eval 1) {System.out.println(线程 threadName 释放锁成功☆☆☆);} else {System.out.println(线程 threadName 释放锁失败该线程未持有锁);}}private byte[] encode(String param) {return param.getBytes();}为什么使用 lua脚本因为保证原子性因为是两个操作如果分两步那就是get k // 进行秘钥 value的比对 del k // 比对成功后删除k如果第一步比对成功后第二步还没来得及执行的时候锁到期然后紧接着别的线程获取到锁里边的 uuid已经变了也就是说持有锁的线程已经不是该线程了此时再执行第二步的删除锁操作肯定是错误的了。5.分布式锁特性4之可重入性作为一把锁我们在使用 synchronized、ReentrantLock的时候是不是有可重入性那咱们这把分布式锁该如何实现可重入呢如果 A线程的锁方法逻辑中调用了 x()方法x()方法中也需要获取这把锁按照这个逻辑x()方法中的锁应该重入进去即可那是不是需要将刚才生成的这个 UUID秘钥传递给 x()方法怎么传递用参数传递就会侵入业务代码5-1 不侵入业务代码实现可重入Thread-Id我们主要是想给上锁的 A线程设置一个只有它自己知道的秘钥把思路时钟往回拨想想线程本身的 idThread.currentThread().getId()是不是就是一个唯一标识呢我们把秘钥 value设置为线程的 id不就行了。String releaseLock_lua if redis.call(\get\,KEYS[1]) ARGV[1] \n then\n return redis.call(\del\, KEYS[1])\n else\n return 0\n end;String addLockLife_lua if redis.call(\exists\, KEYS[1]) 1\n then\n return redis.call(\expire\, KEYS[1], ARGV[1])\n else\n return 0\n end;publicvoidlock() {// 判断是否可重入if (isHeldByCurrentThread()) {return;}while (!tryLock()) {try {Thread.sleep(50); // 自旋} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(线程 threadName 占锁成功★★★);}// 是否是当前线程占有锁同时将超时时间重新设置这个很重要同样也是原子操作privatebooleanisHeldByCurrentThread() {Listbyte[] keys Arrays.asList(encode(lock_key));Listbyte[] args Arrays.asList(encode(String.valueOf(threadId)), encode(String.valueOf(1)));long eval (Long) jedis.eval(encode(addLockLife_lua), keys, args);returneval 1;}privatebooleantryLock(String uuid) {SetParams setParams newSetParams();setParams.ex(1); // 超时时间1ssetParams.nx(); // nxString response jedis.set(lock_key, String.valueOf(threadId), setParams); // 转换为redis命令就是set zjt-key xxx ex 1 nxreturnOK.equals(response);}publicvoidunlock(String uuid) {Listbyte[] keys Arrays.asList(encode(lock_key));Listbyte[] args Arrays.asList(encode(String.valueOf(threadId)));// 使用lua脚本保证原子性long eval (Long) jedis.eval(encode(releaseLock_lua), keys, args);if (eval 1) {System.out.println(线程 threadName 释放锁成功☆☆☆);} else {System.out.println(线程 threadName 释放锁失败该线程未持有锁);}}private byte[] encode(String param) {return param.getBytes();}5-2 Thread-Id 真能行吗不行。想想我们说一个 Thread的id是唯一的是在同一个 JVM进程中是在一个操作系统中也就是在一个机器中。而现实是我们的部署是集群部署多个实例节点那意味着会存在这样一种情况S1机器上的线程上锁成功此时锁中秘钥 value是线程id1如果说同一时间 S2机器中正好线程id1的线程尝试获得这把锁比对秘钥发现成功结果也重入了这把锁也开始执行逻辑此时我们的分布式锁崩溃怎么解决我们只需要在每个节点中维护不同的标识即可怎么维护呢应用启动的时候使用 UUID生成一个唯一标识 APP_ID放在内存中或者使用zookeeper去分配机器id等等。此时我们的秘钥 value这样存即可APP_IDThreadId// static变量final修饰加载在内存中JVM进程生命周期中不变privatestatic final StringAPP_ID UUID.randomUUID().toString();String releaseLock_lua if redis.call(\get\,KEYS[1]) ARGV[1] \n then\n return redis.call(\del\, KEYS[1])\n else\n return 0\n end;String addLockLife_lua if redis.call(\exists\, KEYS[1]) 1\n then\n return redis.call(\expire\, KEYS[1], ARGV[1])\n else\n return 0\n end;publicvoidlock() {// 判断是否可重入if (isHeldByCurrentThread()) {return;}while (!tryLock()) {try {Thread.sleep(50); // 自旋} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(线程 threadName 占锁成功★★★);}// 是否是当前线程占有锁同时将超时时间重新设置这个很重要同样也是原子操作privatebooleanisHeldByCurrentThread() {Listbyte[] keys Arrays.asList(encode(lock_key));Listbyte[] args Arrays.asList(encode(APP_ID String.valueOf(threadId)), encode(String.valueOf(1)));long eval (Long) jedis.eval(encode(addLockLife_lua), keys, args);returneval 1;}privatebooleantryLock(String uuid) {SetParams setParams newSetParams();setParams.ex(1); // 超时时间1ssetParams.nx(); // nxString response jedis.set(lock_key, APP_ID String.valueOf(threadId), setParams); // 转换为redis命令就是set zjt-key xxx ex 1 nxreturnOK.equals(response);}publicvoidunlock(String uuid) {Listbyte[] keys Arrays.asList(encode(lock_key));Listbyte[] args Arrays.asList(encode(APP_ID String.valueOf(threadId)));// 使用lua脚本保证原子性long eval (Long) jedis.eval(encode(releaseLock_lua), keys, args);if (eval 1) {System.out.println(线程 threadName 释放锁成功☆☆☆);} else {System.out.println(线程 threadName 释放锁失败该线程未持有锁);}}private byte[] encode(String param) {return param.getBytes();}5-3 APP_ID实例唯一标识 ThreadId 还是 UUID 好呢继续听我说如果 A线程执行逻辑中间开启了一个子线程执行任务这个子线程任务中也需要重入这把锁因为子线程获取到的线程 id不一样导致重入失败。那意味着需要将这个秘钥继续传递给子线程JUC中 InheritableThreadLocal 派上用场但是感觉怪怪的因为线程间传递的是父线程的 id。微服务中多服务间调用的话可以借用系统自身有的 traceId作为秘钥即可。比如sgm中的traceId 或者 利用RPC框架的隐式传参「至于选择哪种 value的方式根据实际的系统设计 业务场景选择最合适的即可没有最好只有最合适。」5-4、锁重入的超时时间怎么设置注意我们上边的主要注意力在怎么重入进去而我们这是分布式锁要考虑的事情还有很多重入进去后超时时间随便设吗比方说 A线程在锁方法中调用了 x()方法而 x()方法中也有获取锁的逻辑如果 A线程获取锁后执行过程中到 x()方法时这把锁是要重入进去的但是请注意这把锁的超时时间如果小于第一次上锁的时间比方说 A线程设置的超时时间是 1s在 100ms的时候执行到 x()方法中而 x()方法中设置的超时时间是 100ms那么意味着 100ms之后锁就释放了而这个时候我的 A线程的主方法还没有执行完呢却被重入锁设置的时间搞坏了这个怎么搞如果说我在内存中设置一个这把锁设置过的最大的超时时间重入的时候判断下传进来的时间我重入时 expire的时候始终设置成最大的时间而不是由重入锁随意降低锁时间导致上一步的主锁出现问题放在内存中行吗我们上边举例中调用的 x()方法是在一个 JVM中如果是调用远程的一个 RPC服务呢像这种调用的话就需要将秘钥value通过 RpcContext传递过去了到另一个节点的服务中进行锁重入这个时间依然是要用当前设置过锁的最大时间的所以这个最大的时间要存在 redis中而非 JVM内存中经过这一步的分析我们的重入 lua脚本就修改为这样了 ADD_LOCK_LIFE(if redis.call(\get\, KEYS[1]) ARGV[1]\n // 判断是否是锁持有者then\n local thisLockMaxTimeKeepKeyKEYS[1] .. \:maxTime\\n// 记录锁最大时间的key是锁名字:maxTime local nowTimetonumber(ARGV[2])\n// 当前传参进来的time local maxTimeredis.call(\incr\, thisLockMaxTimeKeepKey)\n// 取出当前锁设置的最大的超时时间如果这个保持时间的key不存在返回的是字符串nil这里为了lua脚本的易读性用incr操作这样读出来的都是number类型的操作 local bigerTimemaxTime\n// 临时变量bigerTimemaxTime if nowTimemaxTime-1\n// 如果传参进来的时间记录的最大时间 then\n bigerTimenowTime\n// 则更新bigerTime redis.call(\set\, thisLockMaxTimeKeepKey, tostring(bigerTime))\n// 设置超时时间为最大的time是最安全的 else \n redis.call(\decr\, thisLockMaxTimeKeepKey)\n// 当前传参timemaxTime将刚才那次incr减回来 end\n return redis.call(\expire\, KEYS[1], tostring(bigerTime))\n// 重新设置超时时间为当前锁过的最大的timeelse\n return 0\n end),其实还有另外一种方案比较简单就是锁的超时时间第一次上锁的时间后面所有重入锁的时间。也就是expire 主ttl 重入exipre这种方案是放大的思想一放大就又有上边提到过的一个问题expire太大怎么办参考上边。5-5、重入锁的方法中直接执行 unlock考虑重入次数A线程执行一共需要500ms执行中需要调用 x()方法x()方法中有一个重入锁执行用了 50ms然后执行完后x()方法的 finally{} 块中将锁进行释放。为啥能释放掉因为秘钥我有匹配成功了我就直接释放了。这当然是有问题的所以我们要通过锁重入次数来进行释放锁时候的判断也就是说上锁的时候需要多维护一个 key来保存当前锁的重入次数如果执行释放锁时先进行重入次数 -1-1后如果是0可以直接 del如果0说明还有重入的锁在不能直接 del。5-6 考虑如何存储锁的属性锁的key 重入次数key 最大超时时间key目前为止算上上一步中设置最大超时时间的key加上这一步重入次数的key加上锁本身的key已经有3个key需要注意的事情是这三个key的超时时间是都要设置的为什么假如说重入次数的 key没有设置超时时间服务A节点中在一个JVM中重入了5次后调用一次 RPC服务RPC服务中同样重入锁此时锁重入次数是 6这个时候A服务宕机就意味着无论怎样这把锁不可能释放了这个分布式锁提供的完整能力全线不可用了所以这几个 key是要设置超时时间的怎么设置我上一个锁要维护这么多 key的超时时间太复杂了吧多则乱则容易出问题。怎么办我们想一下是不是最大超时时间的 key和重入次数的 key都附属于锁它们都是锁的属性如果锁不在了谈它们就毫无意义这个时候用什么存储呢redis的 hash数据结构就可以做key是锁里边的 hashKey分别是锁的属性 hashValue是属性值超时时间只设置锁本身 key就可以了。这个时候我们的锁的数据结构就要改变一下了。6、如何解决过期时间确定和业务执行时长不确定性的问题看门狗机制3-2中设置超时时间那里我们预估锁方法执行时间是 200ms我们放大 5倍后设置超时时间是 1s过期时间确定。假想一下如果生产环境中锁方法中的 IO操作极端情况下超时严重比方说 IO就消耗了 2s业务执行时长不确定那就意味着在这次 IO还没有结束的时候我这把锁已经到期释放掉了就意味着别的线程趁虚而入分布式锁崩溃我们要做的是一把分布式锁想要的目的是同一时刻只有一个线程持有锁作为服务而言这个锁现在不管是被哪个线程上锁成功了我服务应该保证这个线程执行的安全性怎么办锁续命看门狗机制。什么意思一旦这把锁出现了上锁操作就意味着这把锁开始投入使用这时我的服务中需要有一个 daemon线程定时去守护我的锁的安全性怎么守护比如说锁超时时间设置的是 1s那么我这个定时任务是每隔 300ms去 redis服务端做一次检查如果我还持有你就给我续命就像 session会话的活跃机制一样。看个例子我上锁时候超时时间设置的是 1s实际方法执行时间是 3s这中间我的定时线程每隔 300ms就会去把这把锁的超时时间重新设置为 1s每隔 300ms一次成功将锁续命成功。publicclassRedisLockIdleThreadPool {private String threadAddLife_lua if redis.call(\exists\, KEYS[1]) 1\n then\n return redis.call(\expire\, KEYS[1], ARGV[1])\n else\n return 0\n end;privatevolatile ScheduledExecutorService scheduledThreadPool;publicRedisLockIdleThreadPool() {if (scheduledThreadPool null) {synchronized (this) {if (scheduledThreadPool null) {scheduledThreadPool Executors.newSingleThreadScheduledExecutor(); // 我这样创建线程池是为了代码的易读性大家务必使用ThreadPoolExecutor去创建scheduledThreadPool.scheduleAtFixedRate(() - {addLife();}, 0, 300, TimeUnit.MILLISECONDS);}}}}privatevoidaddLife() {// ... 省略jedis的初始化过程Listbyte[] keys Arrays.asList(RedisLock.lock_key.getBytes());Listbyte[] args Arrays.asList(String.valueOf(1).getBytes());jedis.eval(threadAddLife_lua.getBytes(), keys, args);}}这就行吗还不行为啥想一下如果每个服务中都像这样去续命锁假如说A服务还在执行过程中的时候还没有执行完就是说还没有手动释放锁的时候宕机此时 redis中锁还在有效期。服务B 也一直在续命这把锁此时这把锁一直在续命但是 B的这个续命一直续的是 A当时设的锁这不是扯吗我自己在不断续命导致我的服务上一直获取不到锁实际上 A已经宕机了呀该释放了不应该去续命了这不是我服务 B该干的活续命的前提是得判断是不是当前进程持有的锁也就是我们的 APP_ID如果不是就不进行续命。续命锁的 lua脚本发生改变如下 THREAD_ADD_LIFE(local vredis.call(\get\, KEYS[1]) \n // get keyif vfalse \n// 如果不存在key读出结果v是falsethen \n // 不存在不处理else \n local match string.find(v, ARGV[1]) \n// 存在判断是否能和APP_ID匹配匹配不上时match是nil if match\nil\\n then \n else \n return redis.call(\expire\, KEYS[1], ARGV[2]) \n// 匹配上了返回的是索引位置如果匹配上了意味着就是当前进程占有的锁就延长时间 end \n end)6-1 锁在我手里我挂了这... 没救。只能等待锁超时释放即便设置了一个很合理的 expire比如 10s但是线上如果真出现了A节点刚拿到锁就宕机了那其他节点也只能干等10s之后才能拿到锁。主要还是业务能不能接受。而如果是 To C的业务中大部分场景无法接受的因为可能会导致用户流失。所以我们需要另外一个监控服务定时去监控 redis中锁的获得者的健康状态如果获取者超过n次无法通信由监控服务负责将锁摘除掉让别的线程继续去获取到锁去干活。
文章转载自:
http://www.morning.ltpzr.cn.gov.cn.ltpzr.cn
http://www.morning.khntd.cn.gov.cn.khntd.cn
http://www.morning.dhnqt.cn.gov.cn.dhnqt.cn
http://www.morning.fewhope.com.gov.cn.fewhope.com
http://www.morning.znknj.cn.gov.cn.znknj.cn
http://www.morning.jnbsx.cn.gov.cn.jnbsx.cn
http://www.morning.bwfsn.cn.gov.cn.bwfsn.cn
http://www.morning.xq3nk42mvv.cn.gov.cn.xq3nk42mvv.cn
http://www.morning.ymrq.cn.gov.cn.ymrq.cn
http://www.morning.daxifa.com.gov.cn.daxifa.com
http://www.morning.zcfmb.cn.gov.cn.zcfmb.cn
http://www.morning.mjzgg.cn.gov.cn.mjzgg.cn
http://www.morning.wmrgp.cn.gov.cn.wmrgp.cn
http://www.morning.qsy36.cn.gov.cn.qsy36.cn
http://www.morning.gbkkt.cn.gov.cn.gbkkt.cn
http://www.morning.hhxwr.cn.gov.cn.hhxwr.cn
http://www.morning.taojava.cn.gov.cn.taojava.cn
http://www.morning.bmhc.cn.gov.cn.bmhc.cn
http://www.morning.mfsjn.cn.gov.cn.mfsjn.cn
http://www.morning.nbybb.cn.gov.cn.nbybb.cn
http://www.morning.fnzbx.cn.gov.cn.fnzbx.cn
http://www.morning.njfgl.cn.gov.cn.njfgl.cn
http://www.morning.fqpyj.cn.gov.cn.fqpyj.cn
http://www.morning.rwmp.cn.gov.cn.rwmp.cn
http://www.morning.nwbnt.cn.gov.cn.nwbnt.cn
http://www.morning.tllhz.cn.gov.cn.tllhz.cn
http://www.morning.tralution.cn.gov.cn.tralution.cn
http://www.morning.hhzdj.cn.gov.cn.hhzdj.cn
http://www.morning.fdxhk.cn.gov.cn.fdxhk.cn
http://www.morning.qllcm.cn.gov.cn.qllcm.cn
http://www.morning.zlmbc.cn.gov.cn.zlmbc.cn
http://www.morning.rdwm.cn.gov.cn.rdwm.cn
http://www.morning.qmsbr.cn.gov.cn.qmsbr.cn
http://www.morning.tsnmt.cn.gov.cn.tsnmt.cn
http://www.morning.nlqmp.cn.gov.cn.nlqmp.cn
http://www.morning.gkjyg.cn.gov.cn.gkjyg.cn
http://www.morning.ftsmg.com.gov.cn.ftsmg.com
http://www.morning.dpzcc.cn.gov.cn.dpzcc.cn
http://www.morning.gccdr.cn.gov.cn.gccdr.cn
http://www.morning.wqpr.cn.gov.cn.wqpr.cn
http://www.morning.rrms.cn.gov.cn.rrms.cn
http://www.morning.yrgb.cn.gov.cn.yrgb.cn
http://www.morning.qklff.cn.gov.cn.qklff.cn
http://www.morning.qbgdy.cn.gov.cn.qbgdy.cn
http://www.morning.kqnwy.cn.gov.cn.kqnwy.cn
http://www.morning.fgqbx.cn.gov.cn.fgqbx.cn
http://www.morning.bhqlj.cn.gov.cn.bhqlj.cn
http://www.morning.ndmbz.cn.gov.cn.ndmbz.cn
http://www.morning.rnhh.cn.gov.cn.rnhh.cn
http://www.morning.jnoegg.com.gov.cn.jnoegg.com
http://www.morning.qzfjl.cn.gov.cn.qzfjl.cn
http://www.morning.bctr.cn.gov.cn.bctr.cn
http://www.morning.cmhkt.cn.gov.cn.cmhkt.cn
http://www.morning.qlhwy.cn.gov.cn.qlhwy.cn
http://www.morning.pdbgm.cn.gov.cn.pdbgm.cn
http://www.morning.qwrb.cn.gov.cn.qwrb.cn
http://www.morning.kdxzy.cn.gov.cn.kdxzy.cn
http://www.morning.hsjfs.cn.gov.cn.hsjfs.cn
http://www.morning.wsyst.cn.gov.cn.wsyst.cn
http://www.morning.trrpb.cn.gov.cn.trrpb.cn
http://www.morning.wchsx.cn.gov.cn.wchsx.cn
http://www.morning.hwnnh.cn.gov.cn.hwnnh.cn
http://www.morning.sbqrm.cn.gov.cn.sbqrm.cn
http://www.morning.zpqk.cn.gov.cn.zpqk.cn
http://www.morning.rtsdz.cn.gov.cn.rtsdz.cn
http://www.morning.tdhxp.cn.gov.cn.tdhxp.cn
http://www.morning.rqdx.cn.gov.cn.rqdx.cn
http://www.morning.hqgxz.cn.gov.cn.hqgxz.cn
http://www.morning.ktrzt.cn.gov.cn.ktrzt.cn
http://www.morning.xskbr.cn.gov.cn.xskbr.cn
http://www.morning.lfpdc.cn.gov.cn.lfpdc.cn
http://www.morning.pxlsh.cn.gov.cn.pxlsh.cn
http://www.morning.shinezoneserver.com.gov.cn.shinezoneserver.com
http://www.morning.ppdr.cn.gov.cn.ppdr.cn
http://www.morning.wpqwk.cn.gov.cn.wpqwk.cn
http://www.morning.ghssm.cn.gov.cn.ghssm.cn
http://www.morning.jqpyq.cn.gov.cn.jqpyq.cn
http://www.morning.nwnbq.cn.gov.cn.nwnbq.cn
http://www.morning.gtnyq.cn.gov.cn.gtnyq.cn
http://www.morning.mzkn.cn.gov.cn.mzkn.cn
http://www.tj-hxxt.cn/news/270579.html

相关文章:

  • 制作好的网页怎么变成网站wordpress怎么做积分
  • 海淀做网站哪家公司好门户网站 建设 通知
  • 网站开发研究热点手机移动开发网站
  • 青海餐饮网站建设杭州百度快照推广
  • 想做网站的公司好东营网站建设东营市南一路东营软件园英
  • 广州网站建设策划网站建设服务器在国外如何打击
  • 衡水哪里可以做网站什么域名不用备案
  • 网站建设兼职在哪找雄安建设工程信息网
  • 国内专门做酒的网站平泉市住房和城乡建设局网站
  • php调用网站导航怎么弄o2o系统软件
  • 哪个企业的网站做的比较好交易网站建设需要学什么软件
  • 网站外部链接做多少合适呢网站ip访问做图表
  • 做自我介绍的网站的图片素材专业的企业网站建设公司
  • 南昌网站推广¥做下拉去118cr黄冈网站建设策划
  • 吉林网站建设业务wordpress评论修改
  • 哪里建网站最好靖边商务网站建设
  • 主播做的头像在哪个网站上做的网站打不开怎么解决
  • 杭州微网站开发简易做海报网站
  • 做外贸哪些网站好网站需要多大的空间
  • 桂林北站时刻表邯郸哪里可以做网站
  • wordpress栏目改瀑布网站谷歌seo做哪些
  • 网站运营内容包含哪些注册公司要花多少钱
  • 做外贸网站企业服装网站的建设与管理
  • 网站开发与制作中期报告辽宁大连直客部七部
  • 免费单页网站建设网站建设 佛山市
  • 湖北中英双语网站建设平面设计网页设计专员
  • 网站商城网络整合营销江西宜春市建设局网站
  • 高青云速网站建设wordpress字体目录下
  • 网站续费查询做网站需要多少钱平邑
  • 大连企业招聘网站page to wordpress