如何使用Memecached做为JFINAL的数据缓存技术

11416人阅读
memcached(1)
最近看到nginx的合并回源,这个和下面的思路有点像。不过nginx的思路还是在控制缓存失效时的并发请求,而不是当缓存快要失效时,及时地更新缓存。
nginx合并回源,参考:http://blog.csdn.net/brainkick/article/details/8570698
======================
当Memcached缓存失效时,容易出现高并发的查询DB,导致DB压力骤然上升。
这篇blog主要是探讨如何在缓存将要失效时,及时地更新缓存,而不是如何在缓存失效之后,如何防止高并发的DB查询。
个人认为,当缓存将要失效时,及时地把新的数据刷到memcached里,这个是解决缓存失效瞬间高并发查DB的最好方法。那么如何及时地知道缓存将要失效?
解决这个问题有几种思路:
比如一个key是aaa,失效时间是30s。
1.定期从DB里查询数据,再刷到memcached里
这种方法有个缺点是,有些业务的key可能是变化的,不确定的。
而且不好界定哪些数据是应该查询出来放到缓存中的,难以区分冷热数据。
2.当缓存取到为null时,加锁去查询DB,只允许一个线程去查询DB
这种方式不太靠谱,不多讨论。而且如果是多个web服务器的话,还是有可能有并发的操作。
3.在向memcached写入value时,同时写入当前机器在时间作为过期时间
当get得到数据时,如果当前时间 - 过期时间 & 5s,则后台启动一个任务去查询DB,更新缓存。
当然,这里的后台任务必须保证同一个key,只有一个线程在执行查询DB的任务,不然这个还是高并发查询DB。
缺点是要把过期时间和value合在一起序列化,取出数据后,还要反序列化。很不方便。
网上大部分文章提到的都是前面两种方式,有少数文章提到第3种方式。下面提出一种基于两个key的方法:
4.两个key,一个key用来存放数据,另一个用来标记失效时间
比如key是aaa,设置失效时间为30s,则另一个key为expire_aaa,失效时间为25s。
在取数据时,用multiget,同时取出aaa和expire_aaa,如果expire_aaa的value == null,则后台启动一个任务去查询DB,更新缓存。和上面类似。
对于后台启动一个任务去查询DB,更新缓存,要保证一个key只有一个线程在执行,这个如何实现?
对于同一个进程,简单加锁即可。拿到锁的就去更新DB,没拿到锁的直接返回。
对于集群式的部署的,如何实现只允许一个任务执行?
这里就要用到memcached的add命令了。
add命令是如果不存在key,则设置成功,返回true,如果已存在key,则不存储,返回false。
当get expired_aaa是null时,则add expired_aaa 过期时间由自己灵活处理。比如设置为3秒。
如果成功了,再去查询DB,查到数据后,再set expired_aaa为25秒。set aaa 为30秒。
综上所述,来梳理下流程:
比如一个key是aaa,失效时间是30s。查询DB在1s内。
put数据时,设置aaa过期时间30s,设置expire_aaa过期时间25s;get数据时,multiget &aaa 和 expire_aaa,如果expired_aaa对应的value != null,则直接返回aaa对应的数据给用户。如果expire_aaa返回value == null,则后台启动一个任务,尝试add expire_aaa,并设置超时过间为3s。这里设置为3s是为了防止后台任务失败或者阻塞,如果这个任务执行失败,那么3秒后,如果有另外的用户访问,那么可以再次尝试查询DB。如果add执行成功,则查询DB,再更新aaa的缓存,并设置expire_aaa的超时时间为25s。
5. 时间存到Value里,再结合add命令来保证只有一个线程去刷新数据
最近重新思考了下这个问题。发现第4种两个key的办法比较耗memcached的内存,因为key数翻倍了。结合第3种方式,重新设计了下,思路如下:
仍然使用两个key的方案:
& & __load_{key}
其中,__load_{key} 这个key相当于一个锁,只允许add成功的线程去更新数据,而这个key的超时时间是比较短的,不会一直占用memcached的内存。
在set 到Memcached的value中,加上一个时间,(time, value),time是memcached上的key未来会过期的时间,并不是当前系统时间。当get到数据时,检查时间是否快要超时: time - now & 5 * 1000,假定设置了快要超时的时间是5秒。
&* 如果是,则后台启动一个新的线程:
&* & & 尝试 add __load_{key},
&* & & 如果成功,则去加载新的数据,并set到memcached中。
&* &原来的线程直接返回value给调用者。
按上面的思路,用xmemcached封装了下:
DataLoader,用户要实现的加载数据的回调接口:
public interface DataLoader {
public &T& T load();
}RefreshCacheManager,用户只需要关心这这两个接口函数:
public class RefreshCacheManager {
static public &T& T tryGet(MemcachedClient memcachedClient, final String key, final int expire, final DataLoader dataLoader);
static public &T& T autoRetryGet(MemcachedClient memcachedClient, final String key, final int expire, final DataLoader dataLoader);
}其中autoRetryGet函数如果get到是null,内部会自动重试4次,每次间隔500ms。
RefreshCacheManager内部自动处理数据快过期,重新刷新到memcached的逻辑。
详细的封装代码在这里:
我个人是倾向于第5种方式的,因为很简单,直观。比第4种方式要节省内存,而且不用mget,在使用memcached集群时不用担心出麻烦事。
这种两个key的方式,还有一个好处,就是数据是自然冷热适应的。如果是冷数据,30秒都没有人访问,那么数据会过期。
如果是热门数据,一直有大流量访问,那么数据就是一直热的,而且数据一直不会过期。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1595715次
积分:11451
积分:11451
排名:第1417名
原创:129篇
转载:29篇
评论:343条
阅读:106022
(2)(1)(1)(1)(4)(2)(2)(1)(1)(7)(2)(4)(1)(3)(7)(12)(3)(3)(6)(3)(3)(8)(3)(1)(9)(7)(2)(4)(4)(5)(2)(2)(1)(4)(5)(1)(8)(17)(1)(4)(1)(2)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'Jfinal_weixin已经出了有好一段时间了!一直在关注当中......最近工作上有需要到这个东西,所以,话了两个小时来看看这个东西,看完demo以后,豁然开朗,原理微信和一般的web项目什么的都是一样的!!所以,为了让后面的同学能够少走一些弯路,我觉得我很有必要把这个学习的过程记录下来,然后给大家进行参考,这样能够让更多的人完成这个微信项目的学习,从零开始学习。
在看此博客有什么不懂的地方可以在我的微信公众号或者。 &微信开发交流群:
如何开发微信?,这个东西大家可以去参考TX的那个,今天主要讲的都是Jfinal_weixin这个东西(如何创建一个WEB项目,如何获取Jfinal_weixin的开发包),也就是官网的那个demo。JFinal官网地址:/ & 开源社区&http://git.oschina.net/jfinal/jfinal-weixin
一:开源社区下载jfinal-weixin源码
下载解压的目录如下
二:将jfinal-weixin源码导入到IDE
三:分析源码如何使用jfinal-weixin
1、WeixinConfig &微信开发环境的配置
public class WeixinConfig extends JFinalConfig {
* 如果生产环境配置文件存在,则优先加载该配置,否则加载开发环境配置文件
* @param pro 生产环境配置文件
* @param dev 开发环境配置文件
public void loadProp(String pro, String dev) {
PropKit.use(pro);
catch (Exception e) {
PropKit.use(dev);
public void configConstant(Constants me) {
loadProp("a_little_config_pro.txt", "a_little_config.txt");
me.setDevMode(PropKit.getBoolean("devMode", false));
// ApiConfigKit 设为开发模式可以在开发阶段输出请求交互的 xml 与 json 数据
ApiConfigKit.setDevMode(me.getDevMode());
public void configRoute(Routes me) {
me.add("/msg", WeixinMsgController.class);
me.add("/api", WeixinApiController.class, "/api");
public void configPlugin(Plugins me) {
// C3p0Plugin c3p0Plugin = new C3p0Plugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim());
// me.add(c3p0Plugin);
// EhCachePlugin ecp = new EhCachePlugin();
// me.add(ecp);
public void configInterceptor(Interceptors me) {
public void configHandler(Handlers me) {
public static void main(String[] args) {
JFinal.start("webapp", 80, "/", 5);
以上通过 configRoute 方法配置了访问路由 "/msg" 与 "/api"。项目启动后,在微信服以务器上配置 url:http://域名/msg &填写Token时需要与配置文件(a_little_config.txt)中的保持一次
2、WeixinMsgController 微信消息处理
package com.jfinal.weixin.
import com.jfinal.kit.PropK
import com.jfinal.log.L
import com.jfinal.weixin.sdk.api.ApiC
import com.jfinal.weixin.sdk.jfinal.MsgC
import com.jfinal.weixin.sdk.msg.in.*;
import com.jfinal.weixin.sdk.msg.in.event.*;
import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionR
import com.jfinal.weixin.sdk.msg.out.*;
* 将此 DemoController 在YourJFinalConfig 中注册路由,
* 并设置好weixin开发者中心的 URL 与 token ,使 URL 指向该
* DemoController 继承自父类 WeixinController 的 index
* 方法即可直接运行看效果,在此基础之上修改相关的方法即可进行实际项目开发
public class WeixinMsgController extends MsgController {
static Logger logger = Logger.getLogger(WeixinMsgController.class);
private static final String helpStr = "\t发送 help 可获得帮助,发送\"视频\" 可获取视频教程,发送 \"美女\" 可看美女,发送 music 可听音乐 ,发送新闻可看JFinal新版本消息。公众号功能持续完善中";
* 如果要支持多公众账号,只需要在此返回各个公众号对应的
ApiConfig 对象即可
* 可以通过在请求 url 中挂参数来动态从数据库中获取 ApiConfig 属性值
public ApiConfig getApiConfig() {
ApiConfig ac = new ApiConfig();
// 配置微信 API 相关常量
ac.setToken(PropKit.get("token"));
ac.setAppId(PropKit.get("appId"));
ac.setAppSecret(PropKit.get("appSecret"));
是否对消息进行加密,对应于微信平台的消息加解密方式:
1:true进行加密且必须配置 encodingAesKey
2:false采用明文模式,同时也支持混合模式
ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false));
ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file"));
protected void processInTextMsg(InTextMsg inTextMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inTextMsg);
render(outCustomMsg);
protected void processInVoiceMsg(InVoiceMsg inVoiceMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inVoiceMsg);
render(outCustomMsg);
protected void processInVideoMsg(InVideoMsg inVideoMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inVideoMsg);
render(outCustomMsg);
protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inShortVideoMsg);
render(outCustomMsg);
protected void processInLocationMsg(InLocationMsg inLocationMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inLocationMsg);
render(outCustomMsg);
protected void processInLinkMsg(InLinkMsg inLinkMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inLinkMsg);
render(outCustomMsg);
protected void processInCustomEvent(InCustomEvent inCustomEvent)
logger.debug("测试方法:processInCustomEvent()");
renderNull();
protected void processInImageMsg(InImageMsg inImageMsg)
//转发给多客服PC客户端
OutCustomMsg outCustomMsg = new OutCustomMsg(inImageMsg);
render(outCustomMsg);
* 实现父类抽方法,处理关注/取消关注消息
protected void processInFollowEvent(InFollowEvent inFollowEvent)
if (InFollowEvent.EVENT_INFOLLOW_SUBSCRIBE.equals(inFollowEvent.getEvent()))
logger.debug("关注:" + inFollowEvent.getFromUserName());
OutTextMsg outMsg = new OutTextMsg(inFollowEvent);
outMsg.setContent("这是Jfinal-weixin测试服务&/br&\r\n感谢您的关注");
render(outMsg);
// 如果为取消关注事件,将无法接收到传回的信息
if (InFollowEvent.EVENT_INFOLLOW_UNSUBSCRIBE.equals(inFollowEvent.getEvent()))
logger.debug("取消关注:" + inFollowEvent.getFromUserName());
protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent)
if (InQrCodeEvent.EVENT_INQRCODE_SUBSCRIBE.equals(inQrCodeEvent.getEvent()))
logger.debug("扫码未关注:" + inQrCodeEvent.getFromUserName());
OutTextMsg outMsg = new OutTextMsg(inQrCodeEvent);
outMsg.setContent("感谢您的关注,二维码内容:" + inQrCodeEvent.getEventKey());
render(outMsg);
if (InQrCodeEvent.EVENT_INQRCODE_SCAN.equals(inQrCodeEvent.getEvent()))
logger.debug("扫码已关注:" + inQrCodeEvent.getFromUserName());
protected void processInLocationEvent(InLocationEvent inLocationEvent)
logger.debug("发送地理位置事件:" + inLocationEvent.getFromUserName());
OutTextMsg outMsg = new OutTextMsg(inLocationEvent);
outMsg.setContent("地理位置是:" + inLocationEvent.getLatitude());
render(outMsg);
protected void processInMassEvent(InMassEvent inMassEvent)
logger.debug("测试方法:processInMassEvent()");
renderNull();
* 实现父类抽方法,处理自定义菜单事件
protected void processInMenuEvent(InMenuEvent inMenuEvent)
logger.debug("菜单事件:" + inMenuEvent.getFromUserName());
OutTextMsg outMsg = new OutTextMsg(inMenuEvent);
outMsg.setContent("菜单事件内容是:" + inMenuEvent.getEventKey());
render(outMsg);
protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults)
logger.debug("语音识别事件:" + inSpeechRecognitionResults.getFromUserName());
OutTextMsg outMsg = new OutTextMsg(inSpeechRecognitionResults);
outMsg.setContent("语音识别内容是:" + inSpeechRecognitionResults.getRecognition());
render(outMsg);
protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent)
logger.debug("测试方法:processInTemplateMsgEvent()");
renderNull();
* 实现父类抽方法,处理文本消息
* 本例子中根据消息中的不同文本内容分别做出了不同的响应,同时也是为了测试 jfinal weixin sdk的基本功能:
本方法仅测试了 OutTextMsg、OutNewsMsg、OutMusicMsg 三种类型的OutMsg,
其它类型的消息会在随后的方法中进行测试
protected void processInTextMsg(InTextMsg inTextMsg) {
String msgContent = inTextMsg.getContent().trim();
// 帮助提示
if ("help".equalsIgnoreCase(msgContent) || "帮助".equals(msgContent)) {
OutTextMsg outMsg = new OutTextMsg(inTextMsg);
outMsg.setContent(helpStr);
render(outMsg);
// 图文消息测试
else if ("news".equalsIgnoreCase(msgContent) || "新闻".equalsIgnoreCase(msgContent)) {
OutNewsMsg outMsg = new OutNewsMsg(inTextMsg);
outMsg.addNews("我的微信公众号javenlife", "Jfinal开发微信技术交流","/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsYTQHXuRwoib8YobOfqCbykp3ZSaEk8czAqdkAARU0OdKDtv34F5evFIQ/0?wx_fmt=jpeg", "http://mp./s?__biz=MzA4MDA2OTA0Mg==&mid=&idx=1&sn=d9e615edb6c24b65c4af3e#rd");
outMsg.addNews("我的博客《智慧云端日记》", "现在就加入 JFinal 极速开发世界,节省更多时间去跟女友游山玩水 ^_^", "/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsY9tPDricojmV5xxuLJyibZJXMAdNOx1qbZFcic9SvsPF2fTUnSc9oQB1IQ/0?wx_fmt=jpeg","http://mp./s?__biz=MzA4MDA2OTA0Mg==&mid=&idx=1&sn=06e816e1ba81df0ac0b3bad#rd");
render(outMsg);
// 音乐消息测试
else if ("music".equalsIgnoreCase(msgContent) || "音乐".equals(msgContent)) {
OutMusicMsg outMsg = new OutMusicMsg(inTextMsg);
outMsg.setTitle("When The Stars Go Blue-Venke Knutson");
outMsg.setDescription("建议在 WIFI 环境下流畅欣赏此音乐");
outMsg.setMusicUrl("/When_The_Stars_Go_Blue-Venke_Knutson.mp3");
outMsg.setHqMusicUrl("/When_The_Stars_Go_Blue-Venke_Knutson.mp3");
outMsg.setFuncFlag(true);
render(outMsg);
else if ("美女".equalsIgnoreCase(msgContent)) {
OutNewsMsg outMsg = new OutNewsMsg(inTextMsg);
outMsg.addNews(
"javenlife 宝贝更新喽",
"javenlife 宝贝更新喽,我们只看美女 ^_^",
"/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsYTQHXuRwoib8YobOfqCbykp3ZSaEk8czAqdkAARU0OdKDtv34F5evFIQ/0?wx_fmt=jpeg",
"http://mp./s?__biz=MzA4MDA2OTA0Mg==&mid=&idx=1&sn=4effc3ba1d2f7f2bca0511#rd");
render(outMsg);
// 其它文本消息直接返回原值 + 帮助提示
renderOutTextMsg("\t文本消息已成功接收,内容为: " + inTextMsg.getContent() + "\n\n" + helpStr);
* 实现父类抽方法,处理图片消息
protected void processInImageMsg(InImageMsg inImageMsg) {
OutImageMsg outMsg = new OutImageMsg(inImageMsg);
// 将刚发过来的图片再发回去
outMsg.setMediaId(inImageMsg.getMediaId());
render(outMsg);
* 实现父类抽方法,处理语音消息
protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) {
OutVoiceMsg outMsg = new OutVoiceMsg(inVoiceMsg);
// 将刚发过来的语音再发回去
outMsg.setMediaId(inVoiceMsg.getMediaId());
render(outMsg);
* 实现父类抽方法,处理视频消息
protected void processInVideoMsg(InVideoMsg inVideoMsg) {
/* 腾讯 api 有 bug,无法回复视频消息,暂时回复文本消息代码测试
OutVideoMsg outMsg = new OutVideoMsg(inVideoMsg);
outMsg.setTitle("OutVideoMsg 发送");
outMsg.setDescription("刚刚发来的视频再发回去");
// 将刚发过来的视频再发回去,经测试证明是腾讯官方的 api 有 bug,待 api bug 却除后再试
outMsg.setMediaId(inVideoMsg.getMediaId());
render(outMsg);
OutTextMsg outMsg = new OutTextMsg(inVideoMsg);
outMsg.setContent("\t视频消息已成功接收,该视频的 mediaId 为: " + inVideoMsg.getMediaId());
render(outMsg);
protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg)
OutTextMsg outMsg = new OutTextMsg(inShortVideoMsg);
outMsg.setContent("\t视频消息已成功接收,该视频的 mediaId 为: " + inShortVideoMsg.getMediaId());
render(outMsg);
* 实现父类抽方法,处理地址位置消息
protected void processInLocationMsg(InLocationMsg inLocationMsg) {
OutTextMsg outMsg = new OutTextMsg(inLocationMsg);
outMsg.setContent("已收到地理位置消息:" +
"\nlocation_X = " + inLocationMsg.getLocation_X() +
"\nlocation_Y = " + inLocationMsg.getLocation_Y() +
"\nscale = " + inLocationMsg.getScale() +
"\nlabel = " + inLocationMsg.getLabel());
render(outMsg);
* 实现父类抽方法,处理链接消息
* 特别注意:测试时需要发送我的收藏中的曾经收藏过的图文消息,直接发送链接地址会当做文本消息来发送
protected void processInLinkMsg(InLinkMsg inLinkMsg) {
OutNewsMsg outMsg = new OutNewsMsg(inLinkMsg);
outMsg.addNews("链接消息已成功接收", "链接使用图文消息的方式发回给你,还可以使用文本方式发回。点击图文消息可跳转到链接地址页面,是不是很好玩 :)" , "/mmbiz/zz3Q6WSrzq1ibBkhSA1BibMuMxLuHIvUfiaGsK7CC4kIzeh178IYSHbYQ5eg9tVxgEcbegAu22Qhwgl5IhZFWWXUw/0", inLinkMsg.getUrl());
render(outMsg);
protected void processInCustomEvent(InCustomEvent inCustomEvent)
System.out.println("processInCustomEvent() 方法测试成功");
* 实现父类抽方法,处理关注/取消关注消息
protected void processInFollowEvent(InFollowEvent inFollowEvent) {
OutTextMsg outMsg = new OutTextMsg(inFollowEvent);
outMsg.setContent("感谢关注 JFinal Weixin 极速开发服务号,为您节约更多时间,去陪恋人、家人和朋友 :) \n\n\n " + helpStr);
// 如果为取消关注事件,将无法接收到传回的信息
render(outMsg);
* 实现父类抽方法,处理扫描带参数二维码事件
protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) {
OutTextMsg outMsg = new OutTextMsg(inQrCodeEvent);
outMsg.setContent("processInQrCodeEvent() 方法测试成功");
render(outMsg);
* 实现父类抽方法,处理上报地理位置事件
protected void processInLocationEvent(InLocationEvent inLocationEvent) {
OutTextMsg outMsg = new OutTextMsg(inLocationEvent);
outMsg.setContent("processInLocationEvent() 方法测试成功");
render(outMsg);
protected void processInMassEvent(InMassEvent inMassEvent)
System.out.println("processInMassEvent() 方法测试成功");
* 实现父类抽方法,处理自定义菜单事件
protected void processInMenuEvent(InMenuEvent inMenuEvent) {
renderOutTextMsg("processInMenuEvent() 方法测试成功");
* 实现父类抽方法,处理接收语音识别结果
protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) {
renderOutTextMsg("语音识别结果: " + inSpeechRecognitionResults.getRecognition());
// 处理接收到的模板消息是否送达成功通知事件
protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) {
String status = inTemplateMsgEvent.getStatus();
renderOutTextMsg("模板消息是否接收成功:" + status);
3、WeixinApiController &微信接口处理
package com.jfinal.weixin.
import com.jfinal.kit.PropK
import com.jfinal.weixin.sdk.api.*;
import com.jfinal.weixin.sdk.jfinal.ApiC
public class WeixinApiController extends ApiController {
* 如果要支持多公众账号,只需要在此返回各个公众号对应的
ApiConfig 对象即可
* 可以通过在请求 url 中挂参数来动态从数据库中获取 ApiConfig 属性值
public ApiConfig getApiConfig() {
ApiConfig ac = new ApiConfig();
// 配置微信 API 相关常量
ac.setToken(PropKit.get("token"));
ac.setAppId(PropKit.get("appId"));
ac.setAppSecret(PropKit.get("appSecret"));
是否对消息进行加密,对应于微信平台的消息加解密方式:
1:true进行加密且必须配置 encodingAesKey
2:false采用明文模式,同时也支持混合模式
ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false));
ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file"));
* 获取公众号菜单
public void getMenu() {
ApiResult apiResult = MenuApi.getMenu();
if (apiResult.isSucceed())
renderText(apiResult.getJson());
renderText(apiResult.getErrorMsg());
* 创建菜单
public void createMenu()
String str = "{\n" +
\"button\": [\n" +
\"name\": \"进入理财\",\n" +
\"url\": \"/bajie/enter\",\n" +
\"type\": \"view\"\n" +
\"name\": \"安全保障\",\n" +
\"key\": \"112\",\n" +
\"type\": \"click\"\n" +
\"name\": \"使用帮助\",\n" +
\"url\": \"/footer/cjwt\",\n" +
\"type\": \"view\"\n" +
ApiResult apiResult = MenuApi.createMenu(str);
if (apiResult.isSucceed())
renderText(apiResult.getJson());
renderText(apiResult.getErrorMsg());
* 获取公众号关注用户
public void getFollowers()
ApiResult apiResult = UserApi.getFollows();
renderText(apiResult.getJson());
* 获取用户信息
public void getUserInfo()
ApiResult apiResult = UserApi.getUserInfo("ohbweuNYB_heu_buiBWZtwgi4xzU");
renderText(apiResult.getJson());
* 发送模板消息
public void sendMsg()
String str = " {\n" +
\"touser\":\"ohbweuNYB_heu_buiBWZtwgi4xzU\",\n" +
\"template_id\":\"9SIa8ph1403NEM3qk3z9-go-p4kBMeh-HGepQZVdA7w\",\n" +
\"url\":\"\",\n" +
\"topcolor\":\"#FF0000\",\n" +
\"data\":{\n" +
\"first\": {\n" +
\"value\":\"恭喜你购买成功!\",\n" +
\"color\":\"#173177\"\n" +
\"keyword1\":{\n" +
\"value\":\"去哪儿网发的酒店红包(1个)\",\n" +
\"color\":\"#173177\"\n" +
\"keyword2\":{\n" +
\"value\":\"1元\",\n" +
\"color\":\"#173177\"\n" +
\"remark\":{\n" +
\"value\":\"欢迎再次购买!\",\n" +
\"color\":\"#173177\"\n" +
ApiResult apiResult = TemplateMsgApi.send(str);
renderText(apiResult.getJson());
* 获取参数二维码
public void getQrcode()
String str = "{\"expire_seconds\": 604800, \"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}";
ApiResult apiResult = QrcodeApi.create(str);
renderText(apiResult.getJson());
String str = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"123\"}}}";
ApiResult apiResult = QrcodeApi.create(str);
renderText(apiResult.getJson());
* 长链接转成短链接
public void getShorturl()
String str = "{\"action\":\"long2short\"," +
"\"long_url\":\"/v2/showcase/goods?alias=128wi9shh&spm=h56083&redirect_count=1\"}";
ApiResult apiResult = ShorturlApi.getShorturl(str);
renderText(apiResult.getJson());
* 获取客服聊天记录
public void getRecord()
String str = "{\n" +
\"endtime\" : ,\n" +
\"pageindex\" : 1,\n" +
\"pagesize\" : 10,\n" +
\"starttime\" : \n" +
ApiResult apiResult = CustomServiceApi.getRecord(str);
renderText(apiResult.getJson());
* 获取微信服务器IP地址
public void getCallbackIp()
ApiResult apiResult = CallbackIpApi.getCallbackIp();
renderText(apiResult.getJson());
通过调用 MenuApi、UserApi 等 Api 的相关方法即可获取封装成 ApiResult 对象的结果,使用 render 系列方法即可快捷输出结果
四:运行项目测试
1、修改配置文件如图
例如:我的微信测试号
2、运行项目&
在浏览器中输入:http://localhost/msg 测试 &说明项目启动成功 &欧耶........
3、微信开发模式接口配置信息
& & & 本地测试需要做外网的80端口映射
& &如果出现上图就配置成功的提示 &到此就可以敬请的玩耍了....................
阅读(...) 评论()
友情链接:}

我要回帖

更多关于 数据库缓存技术 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信