需求:
对于任何一个用户量极大的系统来说做好系统保护是非常有必要的,系统保护可以用服务降级、限流、缓存等方式实现。在最近做的一个需求中需要对一些短时间内访问量很大的用户(主要是针对爬虫)做限流。在一定时间内聚合一次用户访问次数,超过阈值则需要启动限流措施了。限流的实现方式用很多种,我为什么要用storm进行实时计算用户访问次数做限流呢?其实是处于以后需求考虑,做限流只是其中一部分功能。后面还会使用storm用于用户行为分析。
基本概念(需要了解strom的同学可以查看相关教程,本次只讲实践):
Storm 分布式计算结构称为 topology(拓扑),由 stream(数据流), spout(数据流的生成者), bolt(运算)组成。
Storm 的核心数据结构是 tuple。 tuple是 包 含 了 一 个 或 者 多 个 键 值 对 的 列 表,Stream 是 由 无 限 制 的 tuple 组 成 的 序 列。
spout 代表了一个 Storm topology 的主要数据入口,充当采集器的角色,连接到数据源,将数据转化为一个个 tuple,并将 tuple 作为数据流进行发射。
bolt 可以理解为计算程序中的运算或者函数,将一个或者多个数据流作为输入,对数据实施运算后,选择性地输出一个或者多个数据流。 bolt 可以订阅多个由 spout 或者其他bolt 发射的数据流,这样就可以建立复杂的数据流转换网络。
整体流程示意图:
strom top图:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kpy.storm</groupId>
<artifactId>kpy.storm</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.storm/storm-core -->
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
spout用于从QMQ中消费用户数据,QMQ是去哪儿开源的一款MQ,有兴趣的同学可以了解下。由于公司部分项目没有开源,此处demo用没有贴出实现消费QMQ的部分,只用固定字符串数据用于模拟,以供学习交流。
UserInfoSpout
package com.kpy.storm.spout;
import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.Map;
/**
* @Package: com.kpy.storm.spout
* @ClassName: UserInfoSpout
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class UserInfoSpout extends BaseRichSpout {
/**
* BaseRichSpout是ISpout接口和IComponent接口的简单实现,接口对用不到的方法提供了默认的实现
*/
private SpoutOutputCollector collector;
private String[] sentences = {
"{\"allianceId\":\"\",\"allianceName\":\"\",\"clientID\":\"09031141310261798371\",\"cticket\":\"9E41E8C6812C4F597F18BF2330E796BA54D709F9F39588029E950F1098FC59C1\",\"id\":\"2d1b86be-0cc0-41a0-96de-399959d84741\",\"interval\":0,\"login\":true,\"loginType\":\"MemberLogin\",\"ouid\":\"\",\"pageId\":\"212094\",\"referer\":\"http://m.ctrip.fat0.qa.nt.ctripcorp.com:15389/webapp/hotel/hoteldetail/387233.html?atime=20190115&daylater=0&days=1&contrl=0&pay=0&discount=&latlon=&listindex=3&userLocationSearch=false&ucity=2\",\"remoteIPAddress\":\"10.32.151.27\",\"requestMethod\":\"POST\",\"requestURL\":\"http://m.ctrip.fat0.qa.nt.ctripcorp.com:15389/webapp/hotel/api/static/abresult\",\"serviceCode\":\"ABTestController.abresultGet\",\"sessionId\":\"219\",\"sid\":\"\",\"sourceId\":\"\",\"startTime\":1547553924585,\"systemCode\":\"09\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"userCityId\":2,\"userId\":\"18100000000\",\"vid\":\"1545130759191.dnb0yy\"}"
};
private int index=0;
private int count=1;
/**
* open()方法中是ISpout接口中定义,在Spout组件初始化时被调用。
* open()接受三个参数:一个包含Storm配置的Map,一个TopologyContext对象,提供了topology中组件的信息,SpoutOutputCollector对象提供发射tuple的方法。
* 在这个例子中,我们不需要执行初始化,只是简单的存储在一个SpoutOutputCollector实例变量。
*/
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
this.collector = collector;
}
/**
* nextTuple()方法是任何Spout实现的核心。
* Storm调用这个方法,向输出的collector发出tuple。
* 在这里,我们只是发出当前索引的句子,并增加该索引准备发射下一个句子。
*/
public void nextTuple() {
this.collector.emit(new Values(sentences[0]));
// index++;
// if (index>=sentences.length) {
// index=0;
// }
System.out.println("第"+count+"次发射");
count ++;
Utils.sleep(1000);
}
/**
* declareOutputFields是在IComponent接口中定义的,所有Storm的组件(spout和bolt)都必须实现这个接口
* 用于告诉Storm流组件将会发出那些数据流,每个流的tuple将包含的字段
*/
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//告诉组件发出数据流包含sentence字段
declarer.declare(new Fields("userInfo"));
}
}
splitbot主要用于解析切分用户数据,聚合维度有clientID、userId、ip、serviceCode,分别表示用户客户端ID \用户id、ip、用户访问接口。
UserInfoSplitBot
package com.kpy.storm.bolt;
import com.alibaba.fastjson.JSON;
import com.kpy.storm.model.RequestContextUserInfo;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
import java.util.Map;
import static com.kpy.storm.model.CommonConstant.*;
/**
* @Package: com.kpy.storm.bolt
* @ClassName: UserInfoSplitBot
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class UserInfoSplitBot extends BaseRichBolt {
//BaseRichBolt是IComponent和IBolt接口的实现
//继承这个类,就不用去实现本例不关心的方法
private OutputCollector collector;
/**
* prepare()方法类似于ISpout 的open()方法。
* 这个方法在blot初始化时调用,可以用来准备bolt用到的资源,比如数据库连接。
* 本例子和SentenceSpout类一样,SplitSentenceBolt类不需要太多额外的初始化,
* 所以prepare()方法只保存OutputCollector对象的引用。
*/
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector=collector;
}
/**
* SplitSentenceBolt核心功能是在类IBolt定义execute()方法,这个方法是IBolt接口中定义。
* 每次Bolt从流接收一个订阅的tuple,都会调用这个方法。
* 本例中,收到的元组中查找“sentence”的值,
* 并将该值拆分成单个的词,然后按单词发出新的tuple。
*/
public void execute(Tuple input) {
String userInfo = input.getStringByField("userInfo");
RequestContextUserInfo contextUserInfo = JSON.parseObject(userInfo, RequestContextUserInfo.class);
//向下一个bolt发射数据
if (contextUserInfo != null) {
collector.emit(new Values(contextUserInfo.getClientID(), contextUserInfo.getUserId(),contextUserInfo.getRemoteIPAddress(),contextUserInfo.getServiceCode() ));
}
}
/**
* plitSentenceBolt类定义一个元组流,每个包含一个字段(“clientID”)。
*/
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields(FILED_CLIENT_ID, FILED_USER_ID, FILED_IP,FILED_SERVICE_CODE));
}
}
countbot用户统计用户浏览次数。
UserInfoCountBot
package com.kpy.storm.bolt;
import org.apache.storm.Config;
import org.apache.storm.Constants;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import static com.kpy.storm.model.CommonConstant.*;
/**
* @Package: com.kpy.storm.bolt
* @ClassName: UserInfoCountBot
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class UserInfoCountBot extends BaseRichBolt {
private OutputCollector collector;
private static final Logger logger = LoggerFactory.getLogger(UserInfoCountBot.class);
private volatile boolean CHANGE_MAP = false;
private Map<String, Long> COUNT_MAP_0 = new HashMap<>();
private Map<String, Long> COUNT_MAP_1 = new HashMap<>();
/**Bolt定时时间 单位秒*/
private long tickTupleTime = 600;
/**
* 大部分实例变量通常是在prepare()中进行实例化,这个设计模式是由topology的部署方式决定的
* 因为在部署拓扑时,组件spout和bolt是在网络上发送的序列化的实例变量。
* 如果spout或bolt有任何non-serializable实例变量在序列化之前被实例化(例如,在构造函数中创建)
* 会抛出NotSerializableException并且拓扑将无法发布。
* 本例中因为HashMap 是可序列化的,所以可以安全地在构造函数中实例化。
* 但是,通常情况下最好是在构造函数中对基本数据类型和可序列化的对象进行复制和实例化
* 而在prepare()方法中对不可序列化的对象进行实例化。
*/
@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
/**
* 在execute()方法中,我们查找的收到的单词的计数(如果不存在,初始化为0)
* 然后增加计数并存储,发出一个新的词和当前计数组成的二元组。
* 发射计数作为流允许拓扑的其他bolt订阅和执行额外的处理。
*/
@Override
public void execute(Tuple input) {
try {
//该处定义了两个Map用于存放count数据
//如果是系统发射的时钟tuple, 则切换map然后发射到下一个节点,再清空当前map
if (isTickTuple(input)) {
if (!CHANGE_MAP) {
logger.debug("clearMapAndSendTuple-COUNT_MAP_0--"+"CHANGE_MAP="+CHANGE_MAP);
CHANGE_MAP = true;
clearMapAndSendTuple(COUNT_MAP_0);
} else {
logger.debug("clearMapAndSendTuple-COUNT_MAP_1--"+"CHANGE_MAP="+CHANGE_MAP);
CHANGE_MAP = false;
clearMapAndSendTuple(COUNT_MAP_1);
}
return;
}
String clientID = input.getStringByField(FILED_CLIENT_ID);
String userId = input.getStringByField(FILED_USER_ID);
String ip = input.getStringByField(FILED_IP);
String serviceCode = input.getStringByField(FILED_SERVICE_CODE);
String unKey = clientID + FILED_SPLIT + userId + FILED_SPLIT + ip+ FILED_SPLIT + serviceCode;
if (CHANGE_MAP) {
countNum(COUNT_MAP_1, unKey);
} else {
countNum(COUNT_MAP_0, unKey);
}
} catch (Exception e) {
logger.info(e.getMessage(), e);
}
}
private void clearMapAndSendTuple(Map<String, Long> map) {
try {
map.forEach((key, value) -> collector.emit(new Values(key, value)));
map.clear();
} catch (Exception e) {
logger.info(e.getMessage(), e);
}
}
private void countNum(Map<String, Long> map, String unKey) {
Long count = map.get(unKey);
if (count == null) {
//如果不存在,初始化为0
count = 0L;
}
//增加计数
count++;
//存储计数
map.put(unKey, count);
}
@Override
public Map<String, Object> getComponentConfiguration() {
Map<String, Object> conf = new HashMap<String, Object>(1);
// 设置本Bolt定时发射数据(所以这个地方我们可以偷偷地进行某些定时处理)
conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, tickTupleTime);
return conf;
}
/**
* 根据传送过来的Tuple,判断本Tuple是否是tickTuple 如果是tickTuple,则触发动作
*
* @param tuple
* @return
*/
public boolean isTickTuple(Tuple tuple) {
return tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID)
&& tuple.getSourceStreamId().equals(Constants.SYSTEM_TICK_STREAM_ID);
}
/**
*
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//声明一个输出流,其中tuple包括了单词和对应的计数,向后发射
//其他bolt可以订阅这个数据流进一步处理
declarer.declare(new Fields(FILED_UN_KEY, FILED_COUNT));
}
}
输出bot
UserReportBolt
package com.kpy.storm.bolt;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Tuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import static com.kpy.storm.model.CommonConstant.*;
/**
* @Package: com.kpy.storm.bolt
* @ClassName: UserReportBolt
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class UserReportBolt extends BaseRichBolt {
private static final Logger logger = LoggerFactory.getLogger(UserReportBolt.class);
private boolean needSendMessage = NEED_SEND_MESSAGE;
private long maxCountNum = MAX_COUNT_NUM;
@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
}
/**
* 这里作主要业务处理,如将数据存入DB hive HBASE 或者REDIS都可以
* 本需求是将消息发送QMQ
* @param input
*/
@Override
public void execute(Tuple input) {
try {
String unKey = input.getStringByField(FILED_UN_KEY);
Long count = input.getLongByField(FILED_COUNT);
if (needSendMessage && count != null && count >= maxCountNum) {
//超过阈值 发送警戒消息
// QmqUtil.sendMessage(SEND_QMQ_MESSAGE_SUBJECT, unKey + FILED_SPLIT + count, USER_UN_MESSAGE_TAG);
}
} catch (Exception e) {
logger.info(e.getMessage(), e);
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//这里是末端bolt,不需要发射数据流,这里无需定义
}
}
userinfoapp 是系统启动类,在这里定义top图。
UserInfoApp
package com.kpy.storm;
import com.kpy.storm.bolt.UserInfoCountBot;
import com.kpy.storm.bolt.UserInfoSplitBot;
import com.kpy.storm.bolt.UserReportBolt;
import com.kpy.storm.spout.UserInfoSpout;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.tuple.Fields;
import org.apache.storm.utils.Utils;
import static com.kpy.storm.model.CommonConstant.*;
/**
* @Package: com.kpy.storm
* @ClassName: UserInfoApp
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class UserInfoApp {
private static final String SENTENCE_SPOUT_ID = "sentence-spout";
private static final String SPLIT_BOLT_ID = "split-bolt";
private static final String COUNT_BOLT_ID = "count-bolt";
private static final String REPORT_BOLT_ID = "report-bolt";
private static final String TOPOLOGY_NAME = "user-count-topology";
public static void main(String[] args) throws Exception{
//实例化spout和bolt
UserInfoSpout spout = new UserInfoSpout();
UserInfoSplitBot splitBolt = new UserInfoSplitBot();
UserInfoCountBot countBolt = new UserInfoCountBot();
UserReportBolt reportBolt = new UserReportBolt();
//创建了一个TopologyBuilder实例
TopologyBuilder builder = new TopologyBuilder();
//TopologyBuilder提供流式风格的API来定义topology组件之间的数据流
//设置两个Executeor(线程),默认一个
builder.setSpout(SENTENCE_SPOUT_ID, spout,1);
//注册一个bolt并订阅sentence发射出的数据流,shuffleGrouping方法告诉Storm要将spout发射的tuple随机均匀的分发给splitBolt的实例
//SplitBolt分割器设置4个Task,4个Executeor(线程)
builder.setBolt(SPLIT_BOLT_ID, splitBolt, 4).setNumTasks(4).shuffleGrouping(SENTENCE_SPOUT_ID);
//fieldsGrouping将含有特定数据的tuple路由到特殊的bolt实例中
//这里fieldsGrouping()方法保证所有“FILED_CLIENT_ID, FILED_USER_ID, FILED_IP, FILED_SERVICE_CODE”字段相同的tuuple会被路由到同一个CountBolt实例中
//CountBolt单词计数器设置4个Executeor(线程)
builder.setBolt(COUNT_BOLT_ID, countBolt, 4).fieldsGrouping(SPLIT_BOLT_ID, new Fields(FILED_CLIENT_ID, FILED_USER_ID, FILED_IP, FILED_SERVICE_CODE));
//globalGrouping是把UserInfoCountBot发射的所有tuple路由到唯一的UserReportBolt
builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID);
//Config类是一个HashMap<String,Object>的子类,用来配置topology运行时的行为
Config config = new Config();
//设置worker数量
////config.setNumWorkers(2);
// if (args != null && args.length > 0 && "local".equalsIgnoreCase(args[0])) {
LocalCluster cluster = new LocalCluster();
//本地提交
cluster.submitTopology(TOPOLOGY_NAME, config, builder.createTopology());
Utils.sleep(10000);
cluster.killTopology(TOPOLOGY_NAME);
cluster.shutdown();
// } else {
// StormSubmitter.submitTopology(TOPOLOGY_NAME, config, builder.createTopology());
// }
}
}
常量类
CommonConstant
package com.kpy.storm.model;
/**
* @Package: com.ctrip.muise.jstorm.constant
* @ClassName: CommonConstant
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class CommonConstant {
public static final String LOCAL = "local";
/**发送限流信息subject**/
public static final String SEND_QMQ_MESSAGE_SUBJECT = "hotel.wireless.h5.visit.statistic";
/**消费subject**/
public static final String TOPIC_NAME = "hotel.wireless.h5.visit";
public static final String USER_UN_MESSAGE_TAG = "userInfoTag";
public static final String FILED_SPLIT = ":";
public static final String FILED_MESSAGE = "message";
public static final String FILED_USER_INFO= "userInfo";
public static final String FILED_CLIENT_ID = "clientID";
public static final String FILED_USER_ID = "userId";
public static final String FILED_UN_KEY = "unKey";
public static final String FILED_COUNT= "count";
public static final String FILED_IP = "ip";
public static final String FILED_SERVICE_CODE = "serviceCode";
/****************************TOP配置项*********************************************************/
public static final String MESSAGE_SPOUT = "hotel_wireless_h5_message_spout";
public static final String SPLIT_BOLT_ID = "hotel_wireless_h5_split_bolt";
public static final String QMQ_BOLT_ID = "hotel_wireless_h5_qmq_bolt";
public static final String COUNT_BOLT_ID = "hotel_wireless_h5_count_bolt";
public static final String REPORT_BOLT_ID = "hotel_wireless_h5_message_bolt";
public static final String TOPOLOGY_NAME = "hotel_wireless_h5_user_topology";
public static final String SPOUT_ID = TOPOLOGY_NAME + "_" + MESSAGE_SPOUT;
/****************************TOP配置项*********************************************************/
/****************************平台自定义配置项*********************************************************/
/**Bolt定时时间 单位秒*/
public static final long TOPOLOGY_TICK_TUPLE_TIME = 6L;
public static final String H5_TOPOLOGY_TICK_TUPLE_TIME = "h5.topology.tick.tuple.time";
/*** 限流次数**/
public static final long MAX_COUNT_NUM = 1000L;
public static final String MAX_COUNT_NUM_ = "max.count.num";
/**是否需要发送限流信息开关*/
public static final boolean NEED_SEND_MESSAGE = true;
public static final String NEED_SEND_MESSAGE_SWITCH = "need.send.message.switch";
/****************************平台自定义配置项*********************************************************/
}
RequestContextUserInfo
package com.kpy.storm.model;
import java.io.Serializable;
/**
* @Package:
* @ClassName: UserRequestInfo
* @Description:
* @Author: yekeping
* @Version: 1.0
*/
public class RequestContextUserInfo implements Serializable{
private String clientID;
private String vid;
private String sessionId;
private String systemCode;
private long interval;
private String serviceCode;
private String requestMethod;
private String requestURL;
private String remoteIPAddress;
private String id;
private String userAgent;
private String referer;
private String sourceId;
private String allianceId;
private String allianceName;
private String sid;
private String ouid;
private String pageId;
private String result;
private String cticket;
private long startTime;
private String userId;
private int userCityId;
private boolean login;
private String loginType;
public String getClientID() {
return clientID;
}
public void setClientID(String clientID) {
this.clientID = clientID;
}
public String getVid() {
return vid;
}
public void setVid(String vid) {
this.vid = vid;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getSystemCode() {
return systemCode;
}
public void setSystemCode(String systemCode) {
this.systemCode = systemCode;
}
public long getInterval() {
return interval;
}
public void setInterval(long interval) {
this.interval = interval;
}
public String getServiceCode() {
return serviceCode;
}
public void setServiceCode(String serviceCode) {
this.serviceCode = serviceCode;
}
public String getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public String getRequestURL() {
return requestURL;
}
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public String getRemoteIPAddress() {
return remoteIPAddress;
}
public void setRemoteIPAddress(String remoteIPAddress) {
this.remoteIPAddress = remoteIPAddress;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getReferer() {
return referer;
}
public void setReferer(String referer) {
this.referer = referer;
}
public String getSourceId() {
return sourceId;
}
public void setSourceId(String sourceId) {
this.sourceId = sourceId;
}
public String getAllianceId() {
return allianceId;
}
public void setAllianceId(String allianceId) {
this.allianceId = allianceId;
}
public String getAllianceName() {
return allianceName;
}
public void setAllianceName(String allianceName) {
this.allianceName = allianceName;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getOuid() {
return ouid;
}
public void setOuid(String ouid) {
this.ouid = ouid;
}
public String getPageId() {
return pageId;
}
public void setPageId(String pageId) {
this.pageId = pageId;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getCticket() {
return cticket;
}
public void setCticket(String cticket) {
this.cticket = cticket;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public int getUserCityId() {
return userCityId;
}
public void setUserCityId(int userCityId) {
this.userCityId = userCityId;
}
public boolean isLogin() {
return login;
}
public void setLogin(boolean login) {
this.login = login;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}