前言

无论我们做什么系统,95%的系统都离不开注册,登录;

而游戏更加关键,频繁登录,并发登录,导量登录;如果登录承载不起来,那么游戏做的再好,都是徒然,进不去啊;

序言

登录所需要的承载,包含程序和数据存储瓶颈,统一都可以看成io瓶颈;

我的登录服务器,操作只是做登录注册和返回服务器列表功能(只要其他负载均衡不讲解,软负载,硬负载);

登录服务器,分不同渠道登录验证,本地渠道验证,如果登录账户不存在,直接注册账户,然后返回token码;

其他服务器只认token登录需求;减少其他服务器的数据库验证,网络传输验证,io等开销;

我的登录服务器设计只接受 http 登录请求;

http不是通过web发出的;只是一个http监听协议而已;

本文,测试结果,

本机测试服务器标准是 I7 8C + 16G,Windows 10,

创建账号消耗4毫秒左右;理论上登录和创建账号是一致结果;

缓存登录,由于减少了数据库检束;

消耗基本是1毫秒左右;

也就说说

登、注的qps= 5000 =1000 / 4 * 20;

缓存登录 qps= 20000 = 1000 / 1 * 20;

--5000 注册,5000 登录,2万缓存登录 qps

数据库设计

userinfo类

  1 package net.sz.test;
  2
  3 import java.io.Serializable;
  4 import javax.persistence.Column;
  5 import javax.persistence.Id;
  6 import javax.persistence.Table;
  7
  8 /**
  9  * 用户信息表
 10  *
 11  * <br>
 12  * author 失足程序员<br>
 13  * blog http://www.cnblogs.com/ty408/<br>
 14  * mail 492794628@qq.com<br>
 15  * phone 13882122019<br>
 16  */
 17 @Table(name = "UserInfo")
 18 public class UserInfo implements Serializable {
 19
 20     private static final long serialVersionUID = -8907709646630947645L;
 21     @Id
 22     private long id;
 23     /*账户名*/
 24     private String userName;
 25     /*账户名小写副本*/
 26     private String userNameLowerCase;
 27     /*密码*/
 28     @Column(nullable = false)
 29     private String userPwd;
 30     /*电话*/
 31     @Column(nullable = false)
 32     private String userPhone;
 33     /*邮件*/
 34     @Column(nullable = false)
 35     private String userMail;
 36     /*创建时间*/
 37     @Column(nullable = false)
 38     private long createTime;
 39     /*最后登录时间*/
 40     @Column(nullable = false)
 41     private long lastLoginTime;
 42     /*状态,1正常,2表示不可登录*/
 43     @Column(nullable = false)
 44     private int Status;
 45     /*登录后生成的*/
 46     private transient String token;
 47     /*生成 token 的时间*/
 48     private transient long tokenTime;
 49     /* 玩家当前登录服务器ID */
 50     private int loginPlayerServerId;
 51     /* 逻辑服务器传递过来的同步时间 */
 52     private transient long lastUplogintime;
 53
 54     public UserInfo() {
 55     }
 56
 57     public long getId() {
 58         return id;
 59     }
 60
 61     public void setId(long id) {
 62         this.id = id;
 63     }
 64
 65     public String getUserName() {
 66         return userName;
 67     }
 68
 69     public void setUserName(String userName) {
 70         this.userName = userName;
 71     }
 72
 73     public String getUserNameLowerCase() {
 74         return userNameLowerCase;
 75     }
 76
 77     public void setUserNameLowerCase(String userNameLowerCase) {
 78         this.userNameLowerCase = userNameLowerCase;
 79     }
 80
 81     public String getUserPwd() {
 82         return userPwd;
 83     }
 84
 85     public void setUserPwd(String userPwd) {
 86         this.userPwd = userPwd;
 87     }
 88
 89     public String getUserPhone() {
 90         return userPhone;
 91     }
 92
 93     public void setUserPhone(String userPhone) {
 94         this.userPhone = userPhone;
 95     }
 96
 97     public String getUserMail() {
 98         return userMail;
 99     }
100
101     public void setUserMail(String userMail) {
102         this.userMail = userMail;
103     }
104
105     public long getCreateTime() {
106         return createTime;
107     }
108
109     public void setCreateTime(long createTime) {
110         this.createTime = createTime;
111     }
112
113     public long getLastLoginTime() {
114         return lastLoginTime;
115     }
116
117     public void setLastLoginTime(long lastLoginTime) {
118         this.lastLoginTime = lastLoginTime;
119     }
120
121     public int getStatus() {
122         return Status;
123     }
124
125     public void setStatus(int Status) {
126         this.Status = Status;
127     }
128
129     public String getToken() {
130         return token;
131     }
132
133     public void setToken(String token) {
134         this.token = token;
135     }
136
137     public long getLastUplogintime() {
138         return lastUplogintime;
139     }
140
141     public void setLastUplogintime(long lastUplogintime) {
142         this.lastUplogintime = lastUplogintime;
143     }
144
145     public long getTokenTime() {
146         return tokenTime;
147     }
148
149     public void setTokenTime(long tokenTime) {
150         this.tokenTime = tokenTime;
151     }
152
153     public int getLoginPlayerServerId() {
154         return loginPlayerServerId;
155     }
156
157     public void setLoginPlayerServerId(int loginPlayerServerId) {
158         this.loginPlayerServerId = loginPlayerServerId;
159     }
160
161     @Override
162     public String toString() {
163         return "UserInfo{" + "id=" + id + ", userName=" + userName + ", userNameLowerCase=" + userNameLowerCase + ", userPwd=" + userPwd + ", userPhone=" + userPhone + ", userMail=" + userMail + ", createTime=" + createTime + ", lastLoginTime=" + lastLoginTime + ", Status=" + Status + ", token=" + token + ", tokenTime=" + tokenTime + ", loginPlayerServerId=" + loginPlayerServerId + ", lastUplogintime=" + lastUplogintime + '}';
164     }
165
166 }

View Code

用来记录账户数据的;

登录功能划分设计

渠道登录脚本接口设计

 1 package net.sz.game.login.logins.iscript;
 2
 3 import net.sz.framework.nio.http.NioHttpRequest;
 4 import net.sz.framework.scripts.IBaseScript;
 5
 6 /**
 7  *
 8  * <br>
 9  * author 失足程序员<br>
10  * blog http://www.cnblogs.com/ty408/<br>
11  * mail 492794628@qq.com<br>
12  * phone 13882122019<br>
13  */
14 public interface ILoginScriptPlatform extends IBaseScript {
15
16     /**
17      * 处理登录 平台登录
18      *
19      * @param platform 平台ID
20      * @param channelId 渠道ID
21      * @param request 请求
22      * @return
23      */
24     boolean login(int platform, int channelId, NioHttpRequest request);
25 }

View Code

最终本地登录脚本接口设计

 1 package net.sz.game.login.logins.iscript;
 2
 3 import net.sz.framework.nio.http.NioHttpRequest;
 4 import net.sz.framework.scripts.IBaseScript;
 5
 6 /**
 7  *
 8  * <br>
 9  * author 失足程序员<br>
10  * blog http://www.cnblogs.com/ty408/<br>
11  * mail 492794628@qq.com<br>
12  * phone 13882122019<br>
13  */
14 public interface ILoginScript extends IBaseScript {
15
16     /**
17      * 返回错误码
18      *
19      * @param code
20      * @param msg
21      * @return
22      */
23     String getErrorCode(int code, int msg);
24
25     /**
26      * 最终登录
27      *
28      * @param username
29      * @param userpwd
30      * @param platform
31      * @param channelId
32      * @param request
33      */
34     void _login(String username, String userpwd, int platform, int channelId, NioHttpRequest request);
35
36 }

View Code

最终登录脚本需要反向引用,不能通过脚本调用

 1 package net.sz.game.login.logins;
 2
 3 import net.sz.game.login.logins.iscript.ILoginScript;
 4
 5 /**
 6  * 登录管理类
 7  * <br>
 8  * author 失足程序员<br>
 9  * blog http://www.cnblogs.com/ty408/<br>
10  * mail 492794628@qq.com<br>
11  * phone 13882122019<br>
12  */
13 public class LoginManager {
14
15     private static final LoginManager instance = new LoginManager();
16
17
18     public static LoginManager getInstance() {
19         return instance;
20     }
21
22     public ILoginScript loginScript;
23
24 }

View Code

在脚本里面加入

    @Overridepublic void _init() {//反向注册LoginManager.getInstance().loginScript = this;}

直接通过实例对象引用而不再是脚本对象集合调用形式;

脚本登录区分,

100渠道登录

package net.sz.game.login.scripts.logins;import net.sz.framework.nio.http.NioHttpRequest;
import net.sz.framework.szlog.SzLogger;
import net.sz.game.login.logins.LoginManager;
import net.sz.game.login.logins.iscript.ILoginScriptPlatform;/*** 100渠道登录* <br>* author 失足程序员<br>* blog http://www.cnblogs.com/ty408/<br>* mail 492794628@qq.com<br>* phone 13882122019<br>*/
public class LoginScript100 implements ILoginScriptPlatform {private static final SzLogger log = SzLogger.getLogger();//http://127.0.0.1:7073/login?platform=100&channel=100&username=ROBOTsz111&password=1//http://192.168.2.235:7073/login?platform=100&channel=100&username=ROBOT111&password=1&version=1&mac64=jdjdjjd&os=ios&fr=0202125//http://192.168.2.219:7073/login?platform=100&channel=100&username=ROBOT111&password=1&version=1&mac64=jdjdjjd&os=ios&fr=0202125
    @Overridepublic boolean login(int platform, int channelId, NioHttpRequest request) {if (100 == platform) {String username = request.getParam("username");String password = request.getParam("password");LoginManager.getInstance().loginScript._login(username, password, platform, channelId, request);return true;}return false;}
}

View Code

200渠道登录

package net.sz.game.login.scripts.logins;import net.sz.framework.nio.http.NioHttpRequest;
import net.sz.framework.szlog.SzLogger;
import net.sz.game.login.logins.LoginManager;
import net.sz.game.login.logins.iscript.ILoginScriptPlatform;/*** 200渠道登录* <br>* author 失足程序员<br>* blog http://www.cnblogs.com/ty408/<br>* mail 492794628@qq.com<br>* phone 13882122019<br>*/
public class LoginScript200 implements ILoginScriptPlatform {private static final SzLogger log = SzLogger.getLogger();//http://127.0.0.1:7073/login?platform=100&username=ROBOT111&userpwd=1//http://182.150.21.45:7073/login?platform=200&username=ROBOT111&password=1&version=1&mac64=jdjdjjd&os=ios&fr=0202125
    @Overridepublic boolean login(int platform, int channelId, NioHttpRequest request) {if (200 == platform) {String username = request.getParam("username");String password = request.getParam("password");LoginManager.getInstance().loginScript._login(username, password, platform, channelId, request);return true;}return false;}
}

View Code

这时模拟以后接取渠道不同处理形式,

比如ios,360,91,豌豆荚等(拒绝广告);

 1                 NettyHttpServer nioHttpServer = NettyPool.getInstance().addBindHttpServer("0.0.0.0", ServerHttpPort);
 2                 //如果需要加入的白名单
 3                 //nioHttpServer.addWhiteIP("192.168");
 4                 nioHttpServer.addHttpBind((url, request) -> {
 5
 6                     ArrayList<IHttpAPIScript> evts = ScriptManager.getInstance().getBaseScriptEntry().getEvts(IHttpAPIScript.class);
 7                     for (int i = 0; i < evts.size(); i++) {
 8                         IHttpAPIScript get = evts.get(i);
 9                         /*判断监听*/
10                         if (get.checkUrl(url)) {
11                             /*处理监听*/
12                             get.run(url, request);
13                             return;
14                         }
15                     }
16
17                 }, 20, "*");

开启http监听状态;这里可能需要你阅读之前的文章了解底层库支持;

  1 package net.sz.game.login.scripts.logins;
  2
  3 import java.util.List;
  4 import net.sz.framework.nio.http.NioHttpRequest;
  5 import net.sz.framework.scripts.IInitBaseScript;
  6 import net.sz.framework.szlog.SzLogger;
  7 import net.sz.framework.utils.GlobalUtil;
  8 import net.sz.framework.utils.JsonUtil;
  9 import net.sz.framework.utils.MD5Util;
 10 import net.sz.framework.utils.StringUtil;
 11 import net.sz.game.login.data.DataManager;
 12 import net.sz.game.login.logins.LoginManager;
 13 import net.sz.game.login.logins.iscript.ILoginScript;
 14 import net.sz.game.login.service.ServerManager;
 15 import net.sz.game.pmodel.po.loginsr.data.ServerInfo;
 16 import net.sz.game.pmodel.po.loginsr.data.UserInfo;
 17
 18 /**
 19  * 登录本地系统 操作数据库
 20  * <br>
 21  * author 失足程序员<br>
 22  * blog http://www.cnblogs.com/ty408/<br>
 23  * mail 492794628@qq.com<br>
 24  * phone 13882122019<br>
 25  */
 26 public class LoginScript implements ILoginScript, IInitBaseScript {
 27
 28     private static final SzLogger log = SzLogger.getLogger();
 29
 30     private static final String LOGINPWDSIGN = "af0ca5ee6203e02ec076aa8b84385d08";
 31
 32     @Override
 33     public void _init() {
 34         //反向注册
 35         LoginManager.getInstance().loginScript = this;
 36     }
 37
 38     @Override
 39     public String getErrorCode(int code, int msg) {
 40         String ret = "{" + "\"code\":" + code + ", \"msg\":" + msg + "}";
 41         return ret;
 42     }
 43
 44     @Override
 45     public void _login(String username, String userpwd, int platform, int channelId, NioHttpRequest request) {
 46         long currentTimeMillis = System.currentTimeMillis();
 47         if (100 != (platform)) {
 48             username = platform + "_" + username;
 49         }
 50         log.info("登录耗时 " + username + " 1 :" + (System.currentTimeMillis() - currentTimeMillis));
 51         boolean flag = true;
 52         String usernameLowerCase = username.toLowerCase();
 53
 54         if (!StringUtil.checkFilter(username, StringUtil.PATTERN_ABC_0) || !StringUtil.checkFilter(userpwd, StringUtil.PATTERN_ABC_PWD)) {
 55             if (log.isInfoEnabled()) {
 56                 log.info("用户:" + username + " 账号或者密码非法字符!!!");
 57             }
 58             request.addContent(getErrorCode(10, 830510));
 59             flag = false;
 60         }
 61
 62         if (!(100 == platform
 63                 || request.getIp().startsWith("192.168.")
 64                 || request.getIp().startsWith("127.0.0.1"))) {
 65             if (usernameLowerCase.startsWith("robot")) {
 66                 if (log.isInfoEnabled()) {
 67                     log.info("用户:" + username + " 并非特殊平台,不允许此账号!!!");
 68                 }
 69                 request.addContent(getErrorCode(10, 830511));
 70                 flag = false;
 71             }
 72         }
 73         log.info("登录耗时 " + username + " 2 :" + (System.currentTimeMillis() - currentTimeMillis));
 74         if (flag) {
 75             try {
 76
 77                 /*优先获取缓存状态*/
 78                 UserInfo userinfo = DataManager.getInstance().getUserInfoMap().get(usernameLowerCase);
 79
 80                 if (userinfo == null) {
 81                     /*数据库操作之前,加锁*/
 82                     synchronized (this) {
 83                         if (log.isInfoEnabled()) {
 84                             log.info("用户:" + username + " 不存在缓存用户!!!");
 85                         }
 86                         /*再次获取缓存状态,存在并发,那么获得锁权限以后有几率以及得到数据了*/
 87                         userinfo = DataManager.getInstance().getUserInfoMap().get(usernameLowerCase);
 88                         if (userinfo != null) {
 89                             if (log.isInfoEnabled()) {
 90                                 log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 缓存用户!!!");
 91                             }
 92                         } else {
 93                             log.info("登录耗时 " + username + " 3 :" + (System.currentTimeMillis() - currentTimeMillis));
 94                             userinfo = DataManager.getInstance().getDataDao().getObjectByWhere(UserInfo.class, "where `userNameLowerCase` = ?", usernameLowerCase);
 95                             log.info("登录耗时 " + username + " 4 :" + (System.currentTimeMillis() - currentTimeMillis));
 96                             if (userinfo == null) {
 97                                 if (DataManager.getInstance().getUserNameLowerCaseSet().contains(usernameLowerCase)) {
 98                                     request.addContent(getErrorCode(31, 830512));
 99                                     if (log.isInfoEnabled()) {
100                                         log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 注册用户失败,重名!!!");
101                                     }
102                                     return;
103                                 } else {
104
105                                     if ("robottroy".equalsIgnoreCase(usernameLowerCase)) {
106                                         request.addContent(getErrorCode(31, 830513));
107                                         if (log.isInfoEnabled()) {
108                                             log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 注册用户失败,,特殊账号不能注册!!!");
109                                         }
110                                         return;
111                                     }
112
113                                     if (log.isInfoEnabled()) {
114                                         log.info("用户:" + username + " 数据库不存在!!!创建用户");
115                                     }
116
117                                     userinfo = new UserInfo();
118                                     userinfo.setId(GlobalUtil.getId());
119                                     userinfo.setUserName(username);
120                                     userinfo.setUserNameLowerCase(usernameLowerCase);
121                                     userinfo.setUserPwd(userpwd);
122                                     userinfo.setCreateTime(System.currentTimeMillis());
123                                     userinfo.setLastLoginTime(System.currentTimeMillis());
124                                     userinfo.setStatus(1);
125                                     userinfo.setUserMail("");
126                                     userinfo.setUserPhone("");
127                                     userinfo.setPlatformId(platform);
128                                     userinfo.setChannelId(channelId);
129                                     DataManager.getInstance().getcUDThread().insert_Sync(userinfo);
130                                 }
131                             }
132
133                             DataManager.getInstance().getUserNameLowerCaseSet().add(usernameLowerCase);
134
135                             DataManager.getInstance().getUserInfoMap().put(usernameLowerCase, userinfo);
136                             log.info("登录耗时 " + username + " 5 :" + (System.currentTimeMillis() - currentTimeMillis));
137                         }
138                     }
139                 } else {
140                     if (log.isInfoEnabled()) {
141                         log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 缓存用户!!!");
142                     }
143                 }
144
145                 if (userinfo == null || !userinfo.getUserName().equals(username) || !userinfo.getUserPwd().equals(userpwd)) {
146                     request.addContent(getErrorCode(3, 830514));
147                     if (log.isInfoEnabled()) {
148                         log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 用户名或密码错误!!!");
149                     }
150                 } else {
151                     //token生成之后3分钟
152                     long md5time = System.currentTimeMillis();
153                     //String token = MD5Util.md5Encode('=', userinfo.getId() + "", username, request.getIp(), md5time + "", MyAttributeKey.TOKENKEY);
154                     String token = MD5Util.md5Encode('=', userinfo.getId() + "", username, "", md5time + "", LOGINPWDSIGN);
155                     //更新token
156                     userinfo.setToken(token);
157                     //更新token生成时间
158                     userinfo.setTokenTime(md5time);
159                     //更新最后同步时间
160                     userinfo.setLastUplogintime(md5time);
161
162                     userinfo.getLastLoginTime();
163                     userinfo.getLastUplogintime();
164                     log.info("登录耗时 " + username + " 6 :" + (System.currentTimeMillis() - currentTimeMillis));
165                     String serverInfo = ServerManager.getInstance().serverInfoScript.getServerInfo(platform, channelId, request, userinfo);
166                     log.info("登录耗时 " + username + " 7 :" + (System.currentTimeMillis() - currentTimeMillis));
167                     Ret ret = new Ret(0, 0);
168                     ret.setToken(token);
169                     ret.setTime(md5time);
170                     ret.setUserName(username);
171                     ret.setUid(userinfo.getId());
172                     String toJSONString = ret.showString(serverInfo);
173                     log.info("登录耗时 " + username + " 8 :" + (System.currentTimeMillis() - currentTimeMillis));
174                     if (log.isDebugEnabled()) {
175                         log.debug("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 用户登录完成!!!同步服务器信息:" + toJSONString);
176                     }
177                     request.addContent(toJSONString);
178                     log.info("登录耗时 " + username + " 8 :" + (System.currentTimeMillis() - currentTimeMillis));
179                     if (log.isInfoEnabled()) {
180                         log.info("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 用户登录完成!!!");
181                     }
182                 }
183             } catch (Exception e) {
184                 log.error("平台:" + platform + ", ip:" + request.getIp() + ", 用户:" + username + " 登录发生错误信息", e);
185                 request.addContent(getErrorCode(500, 830515));
186             }
187         }
188     }
189
190     public static void main(String[] args) {
191         String jsonString = "{code:0, token:\"af0ca5ee6203e02ec076aa8b84385d08\", userName:\"ROBOTsz111\", msg:\"\", time:1469087482055, uid:197, infos:[{zoneId:100, serverGroup:\"测试大区\", serverId:\"1003\", serverName:\"服务器(刘富顺)\", tcpIp:\"182.150.21.45\", tcpPort:8084, httpIP:\"182.150.21.45\", httpPort:9094, idenIcon:\"\", startTime:\"1\", otherString:\"\", serverState:\"维护\", nextOpenTime:\"\"},{zoneId:200, serverGroup:\"测试专区\", serverId:\"1\", serverName:\"终焉之时\", tcpIp:\"182.150.21.45\", tcpPort:8083, httpIP:\"182.150.21.45\", httpPort:9093, idenIcon:\"new\", startTime:\"1\", otherString:\" \", serverState:\"维护\", nextOpenTime:\" \"},{zoneId:100, serverGroup:\"测试大区\", serverId:\"1001\", serverName:\"服务器(陈飞)\", tcpIp:\"182.150.21.45\", tcpPort:8084, httpIP:\"182.150.21.45\", httpPort:9094, idenIcon:\"\", startTime:\"1\", otherString:\"\", serverState:\"维护\", nextOpenTime:\"\"},{zoneId:100, serverGroup:\"测试大区\", serverId:\"1002\", serverName:\"服务器(吴复全)\", tcpIp:\"182.150.21.45\", tcpPort:8084, httpIP:\"182.150.21.45\", httpPort:9094, idenIcon:\"\", startTime:\"1\", otherString:\"\", serverState:\"维护\", nextOpenTime:\"\"},{zoneId:100, serverGroup:\"测试大区\", serverId:\"2\", serverName:\"客户端\", tcpIp:\"182.150.21.45\", tcpPort:7075, httpIP:\"182.150.21.45\", httpPort:9094, idenIcon:\"xingxing\", startTime:\"1\", otherString:\"\", serverState:\"维护\", nextOpenTime:\"\"}]}";
192         jsonString = new LoginScript().getErrorCode(10, 830510);
193         Ret parseObject = JsonUtil.parseObject(jsonString, Ret.class);
194         log.error(parseObject.toString());
195     }
196
197     static class Ret {
198
199         private int code;
200         private String token;
201         private String userName;
202         private int msg;
203         private long time;
204         private long uid;
205         private ServerInfo[] infos;
206
207         public Ret(int code, int msg) {
208             this.code = code;
209             this.msg = msg;
210         }
211
212         public Ret() {
213         }
214
215         public String showString(String serverinfos) {
216             return "{" + "\"code\":" + code + ", \"token\":\"" + token + "\", \"userName\":\"" + userName + "\", \"msg\":\"" + msg + "\", \"time\":" + time + ", \"uid\":" + uid + ", \"infos\":" + serverinfos + "}";
217         }
218
219         @Override
220         public String toString() {
221             return "{" + "code=" + code + ", token=" + token + ", userName=" + userName + ", msg=" + msg + ", time=" + time + ", uid=" + uid + ", infos=" + infos + '}';
222         }
223
224         /**
225          * @return the code
226          */
227         public int getCode() {
228             return code;
229         }
230
231         /**
232          * @param code the code to set
233          */
234         public void setCode(int code) {
235             this.code = code;
236         }
237
238         /**
239          * @return the token
240          */
241         public String getToken() {
242             return token;
243         }
244
245         /**
246          * @param token the token to set
247          */
248         public void setToken(String token) {
249             this.token = token;
250         }
251
252         /**
253          * @return the userName
254          */
255         public String getUserName() {
256             return userName;
257         }
258
259         /**
260          * @param userName the userName to set
261          */
262         public void setUserName(String userName) {
263             this.userName = userName;
264         }
265
266         /**
267          * @return the msg
268          */
269         public int getMsg() {
270             return msg;
271         }
272
273         /**
274          * @param msg the msg to set
275          */
276         public void setMsg(int msg) {
277             this.msg = msg;
278         }
279
280         /**
281          * @return the time
282          */
283         public long getTime() {
284             return time;
285         }
286
287         /**
288          * @param time the time to set
289          */
290         public void setTime(long time) {
291             this.time = time;
292         }
293
294         /**
295          * @return the uid
296          */
297         public long getUid() {
298             return uid;
299         }
300
301         /**
302          * @param uid the uid to set
303          */
304         public void setUid(long uid) {
305             this.uid = uid;
306         }
307
308         /**
309          * @return the infos
310          */
311         public ServerInfo[] getInfos() {
312             return infos;
313         }
314
315         /**
316          * @param infos the infos to set
317          */
318         public void setInfos(ServerInfo[] infos) {
319             this.infos = infos;
320         }
321
322     }
323
324 }

View Code

整个最后登录流程。设计;

整个登录流程

http 请求 -》 流向 http api -》 httploginscript -》 loginscript渠道登录 -》 loginscript 登录 -》缓存验证 -》 数据库验证 -》 返回结果;

C#代码测试调用

 1 using Net.Sz.Framework.Netty.Http;
 2 using Net.Sz.Framework.Util;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading;
 8 using System.Threading.Tasks;
 9
10
11 namespace CApp_CheckLoginTps
12 {
13
14     class Program
15     {
16
17         static List<int> idList = new List<int>();
18         static IntegerSSId ids = new IntegerSSId();
19
20         static void Main(string[] args)
21         {
22             Console.WriteLine("准备就绪");
23             while (true)
24             {
25                 Console.ReadLine();
26                 Console.WriteLine("注册登录");
27                 test();
28                 Console.ReadLine();
29                 Console.WriteLine("缓存登录");
30                 test2();
31             }
32             Console.ReadLine();
33         }
34
35
36         static void test()
37         {
38             Program.idList.Clear();
39             int tcount = 2;
40             for (int i = 1; i <= tcount; i++)
41             {
42                 new Thread(() =>
43                 {
44                     System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
45                     watch.Start();
46                     int id = ids.GetId();
47                     Program.idList.Add(id);
48                     string ret = HttpClient.UrlGet("http://192.168.2.235:7073/login?platform=100&channel=100&username=" + (id) + "&password=1&version=1&mac64=jdjdjjd&os=ios&fr=0202125");
49                     watch.Stop();
50                     Console.WriteLine(watch.ElapsedMilliseconds);
51                 }).Start();
52             }
53         }
54
55         static void test2()
56         {
57             int tcount = Program.idList.Count;
58
59             for (int i = 0; i < tcount; i++)
60             {
61                 new Thread(new ParameterizedThreadStart((object obj) =>
62                 {
63                     System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
64                     watch.Start();
65                     string ret = HttpClient.UrlGet("http://192.168.2.235:7073/login?platform=100&channel=100&username=" + (obj) + "&password=1&version=1&mac64=jdjdjjd&os=ios&fr=0202125");
66                     watch.Stop();
67                     Console.WriteLine(watch.ElapsedMilliseconds);
68                 })).Start(Program.idList[i]);
69             }
70         }
71     }
72
73 }

View Code

测试结果:

[04-12 15:26:06:408:INFO :LoginScript._login():95] 登录耗时 326060000 4 :5
[04-12 15:26:06:408:INFO :LoginScript._login():114] 用户:326060000 数据库不存在!!!创建用户
[04-12 15:26:06:408:INFO :LoginScript._login():136] 登录耗时 326060000 5 :5
[04-12 15:26:06:408:INFO :LoginScript._login():164] 登录耗时 326060000 6 :5
[04-12 15:26:06:408:INFO :LoginScript._login():166] 登录耗时 326060000 7 :5
[04-12 15:26:06:408:INFO :LoginScript._login():173] 登录耗时 326060000 8 :5
[04-12 15:26:06:408:INFO :LoginScript._login():178] 登录耗时 326060000 8 :5
[04-12 15:26:06:408:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:326060000 用户登录完成!!!
[04-12 15:26:06:409:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:6
[04-12 15:26:07:043:INFO :LoginScript._login():50] 登录耗时 326060000 1 :0
[04-12 15:26:07:043:INFO :LoginScript._login():50] 登录耗时 326060001 1 :0
[04-12 15:26:07:043:INFO :LoginScript._login():73] 登录耗时 326060000 2 :0
[04-12 15:26:07:043:INFO :LoginScript._login():73] 登录耗时 326060001 2 :0
[04-12 15:26:07:043:INFO :LoginScript._login():141] 平台:100, ip:192.168.2.235, 用户:326060000 缓存用户!!!
[04-12 15:26:07:043:INFO :LoginScript._login():141] 平台:100, ip:192.168.2.235, 用户:326060001 缓存用户!!!
[04-12 15:26:07:043:INFO :LoginScript._login():164] 登录耗时 326060000 6 :0
[04-12 15:26:07:043:INFO :LoginScript._login():164] 登录耗时 326060001 6 :0
[04-12 15:26:07:043:INFO :LoginScript._login():166] 登录耗时 326060000 7 :0
[04-12 15:26:07:043:INFO :LoginScript._login():173] 登录耗时 326060000 8 :0
[04-12 15:26:07:043:INFO :LoginScript._login():178] 登录耗时 326060000 8 :0
[04-12 15:26:07:043:INFO :LoginScript._login():166] 登录耗时 326060001 7 :0
[04-12 15:26:07:043:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:326060000 用户登录完成!!!
[04-12 15:26:07:043:INFO :LoginScript._login():173] 登录耗时 326060001 8 :0
[04-12 15:26:07:043:INFO :LoginScript._login():178] 登录耗时 326060001 8 :0
[04-12 15:26:07:043:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:326060001 用户登录完成!!!
[04-12 15:26:07:043:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:1
[04-12 15:26:07:044:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:2

加到并发效果试试

1 [04-12 15:28:34:648:INFO :LoginScript._login():95] 登录耗时 328340007 4 :34
2 [04-12 15:28:34:648:INFO :LoginScript._login():114] 用户:328340007 数据库不存在!!!创建用户
3 [04-12 15:28:34:648:INFO :LoginScript._login():136] 登录耗时 328340007 5 :34
4 [04-12 15:28:34:648:INFO :LoginScript._login():164] 登录耗时 328340007 6 :34
5 [04-12 15:28:34:648:INFO :LoginScript._login():166] 登录耗时 328340007 7 :34
6 [04-12 15:28:34:648:INFO :LoginScript._login():173] 登录耗时 328340007 8 :34
7 [04-12 15:28:34:648:INFO :LoginScript._login():178] 登录耗时 328340007 8 :34
8 [04-12 15:28:34:648:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:328340007 用户登录完成!!!
9 [04-12 15:28:34:649:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:35

当并发加到10的时候,处理登录耗时就出现了;

我把数据库记录手动加到200多万条数据库再次测试一下;

再次尝试注册登录请求的时候

直接导致线程并发死锁;

[04-12 15:41:03:665:INFO :LoginScript._login():95] 登录耗时 340290009 4 :34059
[04-12 15:41:03:665:INFO :LoginScript._login():114] 用户:340290009 数据库不存在!!!创建用户
[04-12 15:41:03:665:INFO :LoginScript._login():136] 登录耗时 340290009 5 :34059
[04-12 15:41:03:666:INFO :LoginScript._login():84] 用户:340290003 不存在缓存用户!!!
[04-12 15:41:03:666:INFO :LoginScript._login():93] 登录耗时 340290003 3 :34056
[04-12 15:41:03:667:INFO :LoginScript._login():164] 登录耗时 340290009 6 :34061
[04-12 15:41:03:668:INFO :LoginScript._login():166] 登录耗时 340290009 7 :34062
[04-12 15:41:03:668:INFO :LoginScript._login():173] 登录耗时 340290009 8 :34062
[04-12 15:41:03:668:INFO :LoginScript._login():178] 登录耗时 340290009 8 :34062
[04-12 15:41:03:668:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:340290009 用户登录完成!!!
[04-12 15:41:03:671:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:34065

我们从抛错和和打印日志情况分析,出现的情况为当操作来了以后,发现缓存不存在,然后进入锁状态,去操作数据库查询;

我们看到登录耗时 4 打印,情况发现查询数据库直接咯嘣;

查询数据是否存在居然耗时34秒;

好吧,数据库原因导致了查询耗时;

通过软件查询,也依然是耗时的,排除程序查询代码性能问题;

然后我们通过分析userinfo类

我们通过对userinfo类的分析,我们只对id字段加入了主键;那么数据库默认对id这个字段加入了索引;

然后我们每一次请求登录的时候数据库检索只能通过userNameLowerCase 字段进行检索;那么考虑对字段加入索引情况;

    @Id@Column(nullable = false, unique = true)private long id;/****/@Column(nullable = false, unique = true)private String userName;/****/@Column(nullable = false, unique = true)private String userNameLowerCase;

我考虑在id,username  userNameLowerCase 三个字段都加入唯一键索引;

我先删除掉数据库,再收到把数据加到200多万测试

在改造了数据库索引后我们

并发下我们还是看出了,登录耗时情况;

看到这里,我们登录的操作,已经是加入缓存处理,数据库索引,提供查询等操作;可并发下还是会耗时呢?

仔细看代码发现

其实我们登录操作, 注册和查询数据库的时候,是需要加锁,保证唯一;

但是我们忽略了一个问题,加锁的时候,其实值加锁,账户的小写副本字符串就可以达到效果了;我这里加入了整个对象锁;锁的范围过大;

                    /*数据库操作之前,加锁,锁定账户小写副本,就一定能针对单账户锁定*/synchronized (usernameLowerCase) {

[04-12 16:11:58:123:INFO :LoginScript._login():95] 登录耗时 411580006 4 :3
[04-12 16:11:58:123:INFO :LoginScript._login():114] 用户:411580006 数据库不存在!!!创建用户
[04-12 16:11:58:124:INFO :LoginScript._login():136] 登录耗时 411580006 5 :4
[04-12 16:11:58:124:INFO :LoginScript._login():95] 登录耗时 411580009 4 :3
[04-12 16:11:58:124:INFO :LoginScript._login():114] 用户:411580009 数据库不存在!!!创建用户
[04-12 16:11:58:124:INFO :LoginScript._login():164] 登录耗时 411580006 6 :4
[04-12 16:11:58:124:INFO :LoginScript._login():136] 登录耗时 411580009 5 :3
[04-12 16:11:58:124:INFO :LoginScript._login():164] 登录耗时 411580009 6 :3
[04-12 16:11:58:124:INFO :LoginScript._login():166] 登录耗时 411580006 7 :4
[04-12 16:11:58:124:INFO :LoginScript._login():173] 登录耗时 411580006 8 :4
[04-12 16:11:58:124:INFO :LoginScript._login():178] 登录耗时 411580006 8 :4
[04-12 16:11:58:124:INFO :LoginScript._login():166] 登录耗时 411580009 7 :3
[04-12 16:11:58:124:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:411580006 用户登录完成!!!
[04-12 16:11:58:124:INFO :LoginScript._login():173] 登录耗时 411580009 8 :3
[04-12 16:11:58:124:INFO :LoginScript._login():178] 登录耗时 411580009 8 :3
[04-12 16:11:58:124:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:411580009 用户登录完成!!!
[04-12 16:11:58:124:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:3
[04-12 16:11:58:124:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:4

现在可以看的出来,我们注册登录耗时,大约4毫秒了;

[04-12 16:12:55:717:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:2
[04-12 16:12:55:717:INFO :LoginScript._login():166] 登录耗时 411580009 7 :0
[04-12 16:12:55:717:INFO :LoginScript._login():173] 登录耗时 411580009 8 :0
[04-12 16:12:55:717:INFO :LoginScript._login():178] 登录耗时 411580009 8 :0
[04-12 16:12:55:717:INFO :LoginScript._login():180] 平台:100, ip:192.168.2.235, 用户:411580009 用户登录完成!!!
[04-12 16:12:55:719:INFO :LoginScript._login():166] 登录耗时 411580006 7 :3
[04-12 16:12:55:719:INFO :LoginScript._login():173] 登录耗时 411580006 8 :3
[04-12 16:12:55:719:INFO :LoginScript._login():178] 登录耗时 411580006 8 :3
[04-12 16:12:55:719:INFO :HttpLoginScript.run():50] 处理一个登陆耗时:2

缓存登录情况;

总结

本次优化的地方,重点在于;

1、防止重复注册依赖数据库检查的是时候,锁对象划分;我们正对账号的小写副本(String) 加锁,是一定能锁定的;

2、加入缓存情况,当前账号登录后,加入滑动缓存,2小时候清理对象;

3、优化数据库方案,加入索引;

4、数据库写入操作,上文一直没讲;这里描述。

以上代码数据库写入操作都是异步的,保证了数据在内存验证通过后,创建对象,异步写入数据库一定能通过数据库验证写入数据库中;

采用集中批量提交数据库方案,提高写入优化功能;

转载于:https://www.cnblogs.com/shizuchengxuyuan/p/6699792.html

net.sz.framework 框架 登录服务器架构 单服2 万 TPS(QPS)相关推荐

  1. net.sz.framework 框架 ORM 消消乐超过亿条数据排行榜分析 天王盖地虎

    序言 天王盖地虎, 老婆马上生孩子了,在家待产,老婆喜欢玩消消乐类似的休闲游戏,闲置状态,无聊的分析一下消消乐游戏的一些技术问题: 由于我主要是服务器研发,客户端属于半吊子,所以就分析一下消消乐排行榜 ...

  2. Spring Framework(框架)整体架构

    原文链接:https://blog.csdn.net/wd2014610/article/details/80061808 Spring 在这个Spring框架大行其道的软件开发世界里,尚有很多工程师 ...

  3. Spring Framework(框架)整体架构(不知道就有些搞笑了哈)

    Spring 在这个Spring框架大行其道的软件开发世界里,尚有很多工程师天天在用,但是从来不会去思考下,Spring框架的整体架构到底是什么样子的啊. 一.首先通过维基百科看看什么是Spring框 ...

  4. 高性能分布式游戏服务器框架,浅谈Go语言自研的分布式游戏服务器架构

    引言:使用Go语言开发游戏已经有5年了,做了三款上线手游,一直采用的都是我们自研的分布式游戏服务器架构.最近我们想把它分享一下,总结一下这几年的经验. 一. 架构图 分布式游戏服务器架构图 1. CD ...

  5. Serverless Framework 无服务器架构

    The Serverless Framework (无服务器架构)允许你自动扩展.按执行付费.将事件驱动的功能部署到任何云. 目前支持 AWS Lambda.Apache OpenWhisk.Micr ...

  6. python异步多线程框架_Python网络编程中的服务器架构(负载均衡、单线程、多线程和同步、异步等)。...

    这篇文章主要介绍服务器架构. 网络服务需要面对两个挑战.第一个问题是核心挑战,要编写出能够正确处理请求并构造合适响应的代码. 第二个挑战是如何将网络代码部署到随系统自动启动的Windows服务或者是U ...

  7. 网狐 协调服务器 作用,网狐架构之登录服务器

    LogonServer.exe 启动过程分析: 1:CLogonServerDlg::OnInitDialog() m_ServiceUnits.SetServiceUnitsSink(this); ...

  8. 无服务器架构_如何开始使用无服务器架构

    无服务器架构 Traditionally, when you wanted to build a web app or API, you'd usually have to spend signifi ...

  9. 服务器控件的优点和缺点_什么是无服务器架构? 它的优点和缺点是什么?

    服务器控件的优点和缺点 Serverless, the new buzzword in town has been gaining a lot of attention from the pros a ...

最新文章

  1. STM32程序进入HardFault_Handler
  2. 疑案追踪:Spring Boot内存泄露排查记
  3. 【Linux】内核态和用户态
  4. Linux,扇区,块,文件系统,目录中的记录项
  5. Spring Boot(三) 将war文件部署到tomcat 、 Thymeleaf示例
  6. php保存ppt,ppt怎么保存到电脑桌面?
  7. windows10环境下VMware14中Ubuntu16.04解决如何上网问题
  8. 关于python中字典描述正确的是_python总结七
  9. 80X86寄存器介绍
  10. localStorage数据丢失
  11. 洛阳师范学院计算机组成原理,洛阳师范学院2010-2011-2《计算机组成原理》试卷b(智爱娟).doc...
  12. 佰落暑期java自学记录-9
  13. 高红梅:第三章 第二节 身份焦虑与英雄梦
  14. 软件设计师备考笔记(炸薯条的视频)
  15. 电脑提示丢失MSVCP140.dll无法启动此程序怎么办【解决方法】
  16. Linux C 函数参考(日期时间)
  17. MySQL like模糊匹配是否走索引
  18. Python|做一个无线弹窗病毒
  19. 单行文本和多行文本溢出显示省略号
  20. 22个高阶布局+配色技巧,才能造就如此高颜值的数据可视化

热门文章

  1. C专家编程--读书笔记九 再论数组
  2. 掌握Tiles框架 (二)-- Tiles布局和定义
  3. NHibernate: Session.Save 采用版本控制时无必要地自动Update版本字段的问题
  4. java ftp connect_java实现ftp的几种方式(第3方包)
  5. matlab 方波_matlab实现方波与三角波波形
  6. 合并单元格两行_28 HTML5标签学习——table单元格的合并
  7. 岳阳机器人餐厅在哪_从机器人咖啡看未来餐饮行业大方向,如何才能活下去?...
  8. 10、计算机图形学——几何介绍(曲面的分类以及示例)
  9. Nacos系列:Nacos的三种部署模式
  10. 如何通过 DJI SDK 控制无人机运动