您的位置:首页 > 移动开发 > 微信开发

微信平台无法使用session的解决方案(Java)

2015-03-11 13:49 826 查看
重新调整了超时清除Session的处理方式,采用一个线程管理所有Session,而不是每一个Session新建一个线程,详情看后边补充部分。

我们知道,微信平台是属于被动式的应答模式,用户发送一条消息或者进行点击菜单上一个按钮,服务器收到请求后进行处理然后回应,整个过程不能超过5秒钟,结束。而要进行较复杂的需要分步骤操作的逻辑时,就需要session机制来辅助,因为我们不能要求用户像我们程序员一样在一行输入一大串参数,那样用户体验太差了,一步一步引导式的操作是最好的。

但是因为微信的所有请求都是通过腾讯的服务器转发的,也就是说所有的请求都是从同一个服务器发来的,这样原本的session机制便失效了,就好比程序原来靠人的长相来分辨和谁在对话,然后所有人的消息都通过一个人转告时,你就无法分辨了。原本的session的识别方式好像是通过cookie,每一次会话生成一个独一无二的sessionID,浏览器的所有请求都会带上这个ID,而腾讯服务器支不支持cookie还不好说,如果不支持的话,那就相当于负责向你转告别人消息的那人是个无头幽灵。。。

这时我们可以换个思路,因为那人在转告时会告诉你是这条消息是谁发过来的,也就是 FromUserName 参数,这是一个独一
4000
无二的openid,相当于sessionID,所以我们可以自己写一个类似于session的模块。

先看代码:

package com.xlzx.wx.session;

/*
* @author zmhawk
* @date 2015-02-09
* /
public class SessionData {
private String name;
private String value;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

}


package com.xlzx.wx.session;

/*
* @author zmhawk
* @date 2015-02-09
* /
import java.util.ArrayList;

public class Session {
private String openid;
private static long time;
private ArrayList<SessionData> sessionData = new ArrayList<SessionData>();

public static long getTime() {
return time;
}
public static void setTime(long time) {
Session.time = time;
}
public ArrayList<SessionData> getSessionData() {
return sessionData;
}
public void setSessionData(ArrayList<SessionData> sessionData) {
this.sessionData = sessionData;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}

//计时器减一分钟
public void timeMin(){
time = time - 60*1000;
}

//重置计时器,10分钟
public void setTime(){
time = 10*60*1000;
}

//判断计时器是否为零
public boolean timeStop(){
if ( time == 0 )
return true;
else
return false;
}

public long whatTime(){
return time;
}

//添加或修改
public void set(SessionData data){
int i = search(data.getName());
setTime();
if ( i<0 ){//添加
sessionData.add(data);
}
else{
sessionData.set(i, data);
}
}

//移除
public void remove(String name){
int i = search(name);
if (i>=0){
sessionData.remove(i);
}
}

public int search(String name){
if (!sessionData.isEmpty()){
setTime();
for ( int i=0 ; i<sessionData.size() ; i++){
if ( sessionData.get(i).getName().equals(name) )
return i;
}
}
return -1;
}

public String get(String name){
int i = search(name);
setTime();
if (i>=0){
return sessionData.get(i).getValue();
}
return null;
}
}


package com.xlzx.wx.session;
/*
* @author zmhawk
* @date 2015-02-09
* /
import java.util.ArrayList;

import com.xlzx.wx.thread.SessionThread;

public class SessionList {
private static ArrayList<Session> sessionList = new ArrayList<Session>();

public static ArrayList<Session> getSessionList() {
return sessionList;
}

public static void setSessionList(ArrayList<Session> sessionList) {
SessionList.sessionList = sessionList;
}

public SessionList(){
}

//查找session中是否有指定项
public static int search (String openid) {
if (sessionList.isEmpty())
return -1;
else {
int size = sessionList.size();
for ( int i=0 ; i<size ; i++){
if ( sessionList.get(i).getOpenid().equals(openid) )
return i;
}
}
return -1;
}

//计时器递减
public static void timeMin(String openid) {
int i = search(openid);
if (i>=0)
sessionList.get(i).timeMin();
}

//判断计时器是否为零
public static boolean timeStop(String openid) {
int i = search(openid);
if ( sessionList.get(i).timeStop() )
return true;
else
return false;
}

//移除session
public static void remove(String openid) {
int i = search(openid);
if (i>=0)
sessionList.remove(i);
}

//向session中存入数据
public static void setSeesion(String name, String value, String openid){
System.out.println("setSeesion " + name + " " + value + " " + openid);
int i = search(openid);
if (i>=0){
SessionData data = new SessionData();
data.setName(name);
data.setValue(value);
sessionList.get(i).set(data);
}
}

//创建session
public static void setSeesion(String openid){
System.out.println("setSeesion " + openid);
int i = search(openid);
if (i<0){
//建立线程
System.out.println("创建计时线程");
Runnable t = new SessionThread(openid);
Thread time = new Thread(t);
time.start();
Session session = new Session();
session.setOpenid(openid);
sessionList.add(session);
}
}

public static void removeData(String name, String openid){
int i = search(openid);
if (i>=0){
sessionList.get(i).remove(name);
}
}

public static String getData(String name, String openid){
System.out.println("getData " + name + " " + openid);
int i = search(openid);
if (i>=0){
String value = sessionList.get(i).get(name);
System.out.println(name + "值为 " + value);
return value;
}
return null;
}

public static boolean sleep(){
return true;
}

public static long getTime(String openid){
int i = search(openid);
if (i>=0){
return sessionList.get(i).whatTime();
}
return 0;
}

}


package com.xlzx.wx.thread;
/**
*计时线程,超时销毁session
*
* @author zmhawk
* @date 2015-02-09
* /
import com.xlzx.wx.session.SessionList;

public class SessionThread implements Runnable{

private String openid;

public SessionThread(String openid){
this.openid = openid;
}

public void run() {

//  while(SessionList.sleep()) {
while( true ){
System.out.println("计时线程启动");
if ( SessionList.search(openid) >= 0 ){
try {
//休眠1分钟
System.out.println("开始休眠");
Thread.sleep(60*1000);
System.out.println("已休眠一分钟,");
if (SessionList.search(openid) >= 0 ){
if ( ! SessionList.timeStop(openid) ){
SessionList.timeMin(openid);   //每隔60秒苏醒一次,将计时器减60秒
}
if ( SessionList.timeStop(openid) ){   /
d014
/如果计时器为零
SessionList.remove(openid);      //删除session
System.out.println("删除session");
}
System.out.println("剩余时间: " + SessionList.getTime(openid)/1000 + "秒");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
break;
}

}
System.out.println("计时线程结束");

}

}


那些System.out是我在调试时监视程序流程的,没删,就一块贴上来了。



上面是这个session的关系图,SessionList是管理session的列表,Session是单个session的,openid是微信用户和微信公众号之间的唯一标识,用来当sessionid,time是剩余时间,也就是剩余多少时间自动销毁,SessionData是session里的各个数据项,name是名称,value是值,如name:“password”,vlaue:”123456”。

在创建一个新session的时候,同步创建一个计时线程, 通过构造函数传入openid值,该计时线程启动后会以60秒为周期休眠,每隔60秒苏醒一次检查对应该openid的session中time的值,若为零则移除session,若不为零则将time减去60*1000毫秒,也就是减去60秒。每对该session执行一次操作(添加、删除数据项、修改、访问)重置time为10分钟

在session和thread的操作中,加入了大量的判断 是否为空、是否有效 的语句,是为了应对各种已知的和未知的异常错误,如数组越界的空指针异常。

SessionList中的接口如下:

public static void setSeesion(String openid)
传入openid,初始化session

public static void setSeesion(String name, String value, String openid)
传入参数,在session中添加项。这个和上面那个是重载的

public static void remove(String openid)
移除session

public static void removeData(String name, String openid)
移除session中name等于传递进来的参数name的那项的value值

public static String getData(String name, String openid)
移除session中name属性的vlaue值,同上类似


下面贴出我程序中的两段代码演示一下用法,供参考:

//若名为"type"属性的vlaue不为空
if ( SessionList.getData("type", fromUserName) != null){
//如果"type"为"bind"
if ( SessionList.getData("type", fromUserName).equals("bind") ){
//调用绑定微信号方法,传入参数中content是用户发来的文本消息内容
respContent = userAction.weixinBind(content, fromUserName);
}
}  else if ( content.equals("绑定" )){
respContent = "请输入学号:";
//创建session
SessionList.setSeesion(fromUserName);
//设定"type"(当前会话类型)为"bind"(绑定微信号操作)
SessionList.setSeesion("type", "bind",fromUserName);
System.out.println("当前状态测试 " + SessionList.getData("type", fromUserName));
}


...

private User user = null;
private UserDAO userdao = null;
private List<User> users = null;

...

public String weixinBind(String text, String openid){
String respContent = "";

if ( SessionList.getData("uid", openid) == null ){
users = userdao.findByUid(text);
if ( users.isEmpty() ){
respContent = "学号不存在!";
SessionList.removeData(text, openid);
} else{
SessionList.setSeesion("uid", text, openid);
respContent = "请输入密码";
}
} else {
String uid = SessionList.getData("uid", openid);
users = userdao.findByUid(uid);
if ( ygusers.isEmpty() ){
respContent = "学号不存在!b";
} else {
System.out.println("f");
user = users.get(0);
if ( user.getPassword().equals(text)){
user.setOpenid(openid);
userdao.update(yguser);
SessionList.remove(openid);
respContent = "绑定成功!";
} else {
respContent = "密码错误,请重新输入!";
SessionList.removeData("password", openid);
}
}
}
return respContent;
}


总结:

本人初学Java不久,好多概念理解还不透彻,所以经常会出现用词不准确和概念描述错误的问题,望指正。

我感觉这段代码肯定还有提升空间,如去除某些不必要的操作和多余的代码,如static对于程序性能影响和是否有必要使用static的问题,比如并发时是否有效我没有测试,比如高并发时的性能,等等还有其它方面我一时想不到的问题,还望能提出建议。

源码下载:

https://code.csdn.net/zmhawk/weixinsessionjava

2015-03-11 第一次修改

之前的代码,对每一个Session都要新建一个线程来处理超时自动清除的功能,这在并发量小,同一时间只有几个人用的时候影响不大,但是一旦并发量较高时,大量的线程势必占用大量的资源,于是我对代码做了如下修改:

创建计时线程:

不再是每新建一个Session时创建一个线程,而是服务器启动时创建一个,由这一个线程管理所有Session,为了优化资源,依然采用周期性休眠的方式,每休眠一分钟,线程苏醒,检查各个Session的剩余时间,到0的清除,没到零的减去60*1000毫秒,也就是减去1分钟。

在服务器启动时启动线程的方法这里不做介绍,仅列出计时线程的代码:

package com.xlzx.wx.thread;
/**
*计时线程,超时销毁session
*
* @author zmhawk
* @date 2015-03-11
* /
import com.xlzx.wx.session.SessionList;

public class SessionThread implements Runnable{

public void run() {
while( true ){
try {
Thread.sleep(60*1000);
SessionList.remove();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


其中,SessionList.remove() 是 SessionList 中新添加的方法,代码如下:

public static void remove(){
Iterator iterator  = sessionList.iterator();
while(iterator.hasNext()){
Session session = (Session) iterator.next();
if(session.timeStop())
iterator.remove();
else{
int i = sessionList.indexOf(session);
session.timeMin();
sessionList.set(i, session);
}
}
}


这里使用了迭代器来遍历 sessionList 。

Session 中的 timeMin(计时器减一分钟) 和 timeStop(判断计时器是否为零) 两个方法要改为 static 。

SessionList 中的 timeMin 和 timeStop 和 getTime ,以及 Session 的whatTime ,他们已经没有利用价值了,可以删了。

咦?才发现原来 SessionList 中也有一个叫 remove 的方法,不过互不干扰哈。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐