learn.lianglianglee.com/专栏/RocketMQ 实战与进阶(完)/06 消息发送常见错误与解决方案.md.html
2022-05-11 18:57:05 +08:00

981 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>06 消息发送常见错误与解决方案.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/01 搭建学习环境准备篇.md.html">01 搭建学习环境准备篇.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/02 RocketMQ 核心概念扫盲篇.md.html">02 RocketMQ 核心概念扫盲篇.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/03 消息发送 API 详解与版本变迁说明.md.html">03 消息发送 API 详解与版本变迁说明.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/04 结合实际应用场景谈消息发送.md.html">04 结合实际应用场景谈消息发送.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/05 消息发送核心参数与工作原理详解.md.html">05 消息发送核心参数与工作原理详解.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/RocketMQ 实战与进阶(完)/06 消息发送常见错误与解决方案.md.html">06 消息发送常见错误与解决方案.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/07 事务消息使用及方案选型思考.md.html">07 事务消息使用及方案选型思考.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/08 消息消费 API 与版本变迁说明.md.html">08 消息消费 API 与版本变迁说明.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/09 DefaultMQPushConsumer 核心参数与工作原理.md.html">09 DefaultMQPushConsumer 核心参数与工作原理.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/10 DefaultMQPushConsumer 使用示例与注意事项.md.html">10 DefaultMQPushConsumer 使用示例与注意事项.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/11 DefaultLitePullConsumer 核心参数与实战.md.html">11 DefaultLitePullConsumer 核心参数与实战.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/12 结合实际场景再聊 DefaultLitePullConsumer 的使用.md.html">12 结合实际场景再聊 DefaultLitePullConsumer 的使用.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/13 结合实际场景顺序消费、消息过滤实战.md.html">13 结合实际场景顺序消费、消息过滤实战.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/14 消息消费积压问题排查实战.md.html">14 消息消费积压问题排查实战.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/15 RocketMQ 常用命令实战.md.html">15 RocketMQ 常用命令实战.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/16 RocketMQ 集群性能摸高.md.html">16 RocketMQ 集群性能摸高.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/17 RocketMQ 集群性能调优.md.html">17 RocketMQ 集群性能调优.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/18 RocketMQ 集群平滑运维.md.html">18 RocketMQ 集群平滑运维.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/19 RocketMQ 集群监控(一).md.html">19 RocketMQ 集群监控(一).md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/20 RocketMQ 集群监控(二).md.html">20 RocketMQ 集群监控(二).md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/21 RocketMQ 集群告警.md.html">21 RocketMQ 集群告警.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/22 RocketMQ 集群踩坑记.md.html">22 RocketMQ 集群踩坑记.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/23 消息轨迹、ACL 与多副本搭建.md.html">23 消息轨迹、ACL 与多副本搭建.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/24 RocketMQ-Console 常用页面指标获取逻辑.md.html">24 RocketMQ-Console 常用页面指标获取逻辑.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/25 RocketMQ Nameserver 背后的设计理念.md.html">25 RocketMQ Nameserver 背后的设计理念.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/26 Java 并发编程实战.md.html">26 Java 并发编程实战.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/27 从 RocketMQ 学基于文件的编程模式(一).md.html">27 从 RocketMQ 学基于文件的编程模式(一).md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/28 从 RocketMQ 学基于文件的编程模式(二).md.html">28 从 RocketMQ 学基于文件的编程模式(二).md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/29 从 RocketMQ 学 Netty 网络编程技巧.md.html">29 从 RocketMQ 学 Netty 网络编程技巧.md.html</a>
</li>
<li>
<a href="/专栏/RocketMQ 实战与进阶(完)/30 RocketMQ 学习方法之我见.md.html">30 RocketMQ 学习方法之我见.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>06 消息发送常见错误与解决方案</h1>
<p>本篇将结合自己使用 RocketMQ 的经验,对消息发送常见的问题进行分享,基本会遵循出现问题,分析问题、解决问题。</p>
<h3>No route info of this topic</h3>
<p>无法找到路由信息,其完整的错误堆栈信息如下:</p>
<p><img src="assets/20200802174912765.png" alt="1" /></p>
<p>而且很多读者朋友会说Broker 端开启了自动创建主题也会出现上述问题。</p>
<p>RocketMQ 的路由寻找流程如下图所示:</p>
<p><img src="assets/20200802174919254.png" alt="2" /></p>
<p>上面的核心关键点如下:</p>
<ul>
<li>如果 Broker 开启了自动创建 Topic在启动的时候会默认创建主题 TBW102并会随着 Broker 发送到 NameServer 的心跳包汇报给 NameServer继而从 NameServer 查询路由信息时能返回路由信息。</li>
<li>消息发送者在消息发送时首先会查本地缓存,如果本地缓存中存在,直接返回路由信息。</li>
<li>如果缓存不存在,则向 NameServer 查询路由信息,如果 NameServer 存在该路由信息,就直接返回。</li>
<li>如果 NameServer 不存在该 Topic 的路由信息,如果没有开启自动创建主题,则抛出 <code>No route info of this topic</code></li>
<li>如果开启了自动创建主题,则使用默认主题向 NameServer 查询路由信息,并使用默认 Topic 的路由信息为自己的路由信息,将不会抛出 <code>No route info of this topic</code></li>
</ul>
<p>通常情况下 <code>No route info of this topic</code> 这个错误一般是在刚搭建 RocketMQ、刚入门 RocketMQ 遇到的比较多。通常的排查思路如下。</p>
<p>\1. 可以通过 RocketMQ-Console 查询路由信息是否存在,或使用如下命令查询路由信息:</p>
<pre><code>cd ${ROCKETMQ_HOME}/bin
sh ./mqadmin topicRoute -n 127.0.0.1:9876 -t dw_test_0003
</code></pre>
<p>其输出结果如下所示:</p>
<p><img src="assets/20200802174928871.png" alt="3" /></p>
<p>\2. 如果通过命令无法查询到路由信息,则查看 Broker 是否开启了自动创建 Topic参数为 autoCreateTopicEnable该参数默认为 true。但在生产环境不建议开启。</p>
<p>\3. 如果开启了自动创建路由信息但还是抛出这个错误这个时候请检查客户端Producer连接的 NameServer 地址是否与 Broker 中配置的 NameServer 地址是否一致。</p>
<p>经过上面的步骤,基本就能解决该错误。</p>
<h3>消息发送超时</h3>
<p>消息发送超时,通常客户端的日志如下:</p>
<p><img src="assets/20200802174935798.png" alt="4" /></p>
<p>客户端报消息发送超时,通常第一怀疑的对象是 RocketMQ 服务器,是不是 Broker 性能出现了抖动,无法抗住当前的量。</p>
<p>那我们如何来排查 RocketMQ 当前是否有性能瓶颈呢?</p>
<p>首先我们执行如下命令查看 RocketMQ 消息写入的耗时分布情况:</p>
<pre><code>cd /${USER.HOME}/logs/rocketmqlogs/
grep -n 'PAGECACHERT' store.log | more
</code></pre>
<p>输出结果如下所示:</p>
<p><img src="assets/2020080217494375.png" alt="5" /></p>
<p>RocketMQ 会每一分钟打印前一分钟内消息发送的耗时情况分布,我们从这里就能窥探 RocketMQ 消息写入是否存在明细的性能瓶颈,其区间如下:</p>
<ul>
<li>[&lt;=0ms] 小于 0ms即微妙级别的</li>
<li>[0~10ms] 小于 10ms 的个数</li>
<li>[10~50ms] 大于 10ms 小于 50ms 的个数</li>
</ul>
<p>其他区间显示,绝大多数会落在微妙级别完成,按照笔者的经验如果 100~200ms 及以上的区间超过 20 个后,说明 Broker 确实存在一定的瓶颈,如果只是少数几个,说明这个是内存或 PageCache 的抖动,问题不大。</p>
<p>通常情况下超时通常与 Broker 端的处理能力关系不大,还有另外一个佐证,在 RocketMQ broker 中还存在快速失败机制,即当 Broker 收到客户端的请求后会将消息先放入队列,然后顺序执行,如果一条消息队列中等待超过 200ms 就会启动快速失败,向客户端返回 <code>[TIMEOUT_CLEAN_QUEUE]broker busy</code>,这个在本专栏的第 3 部分会详细介绍。</p>
<p>在 RocketMQ 客户端遇到网络超时,通常可以考虑一些应用本身的垃圾回收,是否由于 GC 的停顿时间导致的消息发送超时,这个我在测试环境进行压力测试时遇到过,但生产环境暂时没有遇到过,大家稍微留意一下。</p>
<p>在 RocketMQ 中通常遇到网络超时,通常与网络的抖动有关系,但由于我对网络不是特别擅长,故暂时无法找到直接证据,但能找到一些间接证据,例如在一个应用中同时连接了 Kafka、RocketMQ 集群,发现在出现超时的同一时间发现连接到 RocketMQ 集群内所有 Broker连接到 Kafka 集群都出现了超时。</p>
<p><strong>但出现网络超时,我们总得解决,那有什么解决方案吗?</strong></p>
<p>我们对消息中间件的最低期望就是高并发低延迟,从上面的消息发送耗时分布情况也可以看出 RocketMQ 确实符合我们的期望,绝大部分请求都是在微妙级别内,故我给出的方案时,<strong>减少消息发送的超时时间,增加重试次数,并增加快速失败的最大等待时长</strong>。具体措施如下。</p>
<p>\1. 增加 Broker 端快速失败的时长,建议为 1000在 Broker 的配置文件中增加如下配置:</p>
<pre><code>maxWaitTimeMillsInQueue=1000
</code></pre>
<p>主要原因是在当前的 RocketMQ 版本中,快速失败导致的错误为 system_busy并不会触发重试适当增大该值尽可能避免触发该机制详情可以参考本专栏第 3 部分内容,会重点介绍 system_busy、broker_busy。</p>
<p><strong>如果 RocketMQ 的客户端版本为 4.3.0 以下版本(不含 4.3.0</strong></p>
<p>将超时时间设置消息发送的超时时间为 500ms并将重试次数设置为 6 次(这个可以适当进行调整,尽量大于 3其背后的哲学是尽快超时并进行重试因为发现局域网内的网络抖动是瞬时的下次重试的是就能恢复并且 RocketMQ 有故障规避机制,重试的时候会尽量选择不同的 Broker相关的代码如下</p>
<pre><code> DefaultMQProducer producer = new DefaultMQProducer(&quot;dw_test_producer_group&quot;);
producer.setNamesrvAddr(&quot;127.0.0.1:9876&quot;);
producer.setRetryTimesWhenSendFailed(5);// 同步发送模式:重试次数
producer.setRetryTimesWhenSendAsyncFailed(5);// 异步发送模式:重试次数
producer.start();
producer.send(msg,500);//消息发送超时时间
</code></pre>
<p><strong>如果 RocketMQ 的客户端版本为 4.3.0 及以上版本:</strong></p>
<p>如果客户端版本为 4.3.0 及其以上版本,由于其设置的消息发送超时时间为所有重试的总的超时时间,故不能直接通过设置 RocketMQ 的发送 API 的超时时间,而是需要对其 API 进行包装,重试需要在外层收到进行,例如示例代码如下:</p>
<pre><code> public static SendResult send(DefaultMQProducer producer, Message msg, int
retryCount) {
Throwable e = null;
for(int i =0; i &lt; retryCount; i ++ ) {
try {
return producer.send(msg,500); //设置超时时间,为 500ms内部有重试机制
} catch (Throwable e2) {
e = e2;
}
}
throw new RuntimeException(&quot;消息发送异常&quot;,e);
}
</code></pre>
<h3>System busy、Broker busy</h3>
<p>在使用 RocketMQ 中,如果 RocketMQ 集群达到 1W/tps 的压力负载水平System busy、Broker busy 就会是大家经常会遇到的问题。例如如下图所示的异常栈。</p>
<p><img src="assets/2020080217495064.png" alt="6" /></p>
<p><img src="assets/2020080217495726.png" alt="7" /></p>
<p>纵观 RocketMQ 与 System busy、Broker busy 相关的错误关键字,总共包含如下 5 个:</p>
<pre><code>[REJECTREQUEST]system busy
too many requests and system thread pool busy
[PC_SYNCHRONIZED]broker busy
[PCBUSY_CLEAN_QUEUE]broker busy
[TIMEOUT_CLEAN_QUEUE]broker busy
</code></pre>
<h4><strong>原理分析</strong></h4>
<p>我们先用一张图来阐述一下,在消息发送的全生命周期中,分别在什么时候会抛出上述错误。</p>
<p><img src="assets/20200802175003961.png" alt="8" /></p>
<p>根据上述 5 类错误日志,其触发的原由可以归纳为如下 3 种。</p>
<p><strong>1. PageCache 压力较大</strong></p>
<p>其中如下三类错误属于此种情况:</p>
<pre><code>[REJECTREQUEST]system busy
[PC_SYNCHRONIZED]broker busy
[PCBUSY_CLEAN_QUEUE]broker busy
</code></pre>
<p>判断 PageCache 是否忙的依据就是,在写入消息、向内存追加消息时加锁的时间,默认的判断标准是加锁时间超过 1s就认为是 PageCache 压力大,向客户端抛出相关的错误日志。</p>
<p><strong>2. 发送线程池挤压的拒绝策略</strong></p>
<p>在 RocketMQ 中处理消息发送的,是一个只有一个线程的线程池,内部会维护一个有界队列,默认长度为 1W。如果当前队列中挤压的数量超过 1w执行线程池的拒绝策略从而抛出 [too many requests and system thread pool busy] 错误。</p>
<p><strong>3. Broker 端快速失败</strong></p>
<p>默认情况下 Broker 端开启了快速失败机制,就是在 Broker 端还未发生 PageCache 繁忙(加锁超过 1s的情况但存在一些请求在消息发送队列中等待 200ms 的情况RocketMQ 会不再继续排队,直接向客户端返回 System busy但由于 RocketMQ 客户端目前对该错误没有进行重试处理,所以在解决这类问题的时候需要额外处理。</p>
<h4><strong>PageCache 繁忙解决方案</strong></h4>
<p>一旦消息服务器出现大量 PageCache 繁忙(在向内存追加数据加锁超过 1s的情况这个是比较严重的问题需要人为进行干预解决解决的问题思路如下。</p>
<p><strong>1. transientStorePoolEnable</strong></p>
<p>开启 transientStorePoolEnable 机制,即在 Broker 中配置文件中增加如下配置:</p>
<pre><code>transientStorePoolEnable=true
</code></pre>
<p>transientStorePoolEnable 的原理如下图所示:</p>
<p><img src="assets/20200802175012124.png" alt="9" /></p>
<p>引入 transientStorePoolEnable 能缓解 PageCache 的压力背后关键如下:</p>
<ul>
<li>消息先写入到堆外内存中,该内存由于启用了内存锁定机制,故消息的写入是接近直接操作内存,性能可以得到保证。</li>
<li>消息进入到堆外内存后,后台会启动一个线程,一批一批将消息提交到 PageCache即写消息时对 PageCache 的写操作由单条写入变成了批量写入,降低了对 PageCache 的压力。</li>
</ul>
<p>引入 transientStorePoolEnable 会增加数据丢失的可能性,如果 Broker JVM 进程异常退出,提交到 PageCache 中的消息是不会丢失的但存在堆外内存DirectByteBuffer中但还未提交到 PageCache 中的这部分消息将会丢失。但通常情况下RocketMQ 进程退出的可能性不大,通常情况下,如果启用了 transientStorePoolEnable消息发送端需要有重新推送机制补偿思想</p>
<p><strong>2. 扩容</strong></p>
<p>如果在开启了 transientStorePoolEnable 后,还会出现 PageCache 级别的繁忙,那需要集群进行扩容,或者对集群中的 Topic 进行拆分,即将一部分 Topic 迁移到其他集群中,降低集群的负载。关于 RocketMQ 优雅停机、扩容方案等,将在本专栏的<strong>运维实战部分</strong>做专题介绍。</p>
<blockquote>
<p>温馨提示:在 RocketMQ 出现 PageCache 繁忙造成的 Broker busyRocketMQ Client 会有重试机制。</p>
</blockquote>
<h4><strong>TIMEOUT_CLEAN_QUEUE 解决方案</strong></h4>
<p>由于如果出现 TIMEOUT_CLEAN_QUEUE 的错误,客户端暂时不会对其进行重试,故现阶段的建议是适当增加快速失败的判断标准,即在 Broker 的配置文件中增加如下配置:</p>
<pre><code>#该值默认为 200表示 200ms
waitTimeMillsInSendQueue=1000
</code></pre>
<p>温馨提示,关于 Broker busy笔者发表过两篇文章大家也可以结合着看</p>
<ul>
<li><a href="https://mp.weixin.qq.com/s/N_ttVjBpqVUA0CGrOybNLA">RocketMQ 消息发送 System busy、Broker busy 原因分析与解决方案</a></li>
<li><a href="https://mp.weixin.qq.com/s/1yFedcwtQ7mYcuHDvGCrqw">再谈 RocketMQ Broker busy</a></li>
</ul>
<h3>小结</h3>
<p>本篇主要对实际中常遇到的,关于消息发送方面经常遇到的问题进行剖析,从而提出解决方案。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/RocketMQ 实战与进阶(完)/05 消息发送核心参数与工作原理详解.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/RocketMQ 实战与进阶(完)/07 事务消息使用及方案选型思考.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099741d390d3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>