Leopard 应用开发 游戏事业部 卢轩华 2013.12.27. 目录 Leopard 快速开发 Leopard...

Preview:

Citation preview

Leopard 应用开发

游戏事业部 卢轩华

2013.12.27

目录

Leopard 快速开发Leopard 开发规范Leopard 持久化Leopard Web 开发Leopard 其他功能

Leopard 快速开发

Leopard 开发规范

Leopard 持久化

Leopard 持久化 jdbc

在 Spring jdbc 的基础上进行二次封装,降低出错概率…

Leopard 持久化 jdbc 示例

@Overridepublic int add(AdminLog adminLog) {

StringBuilder sql = new StringBuilder();sql.append("insert into ").append(TABLE).append("(admin_id, type, content, add_time, admin_ip,

issue_id) values(?,?,?,?,?,?)");Object[] args = new Object[] { adminLog.getAdminId(),

adminLog.getType().getId(), adminLog.getContent(),adminLog.getAddTime(), adminLog.getAdminIp(),

adminLog.getIssueId() };return this.getJdbcTemplate().update(sql.toString(),

args);}

数据插入 Spring 原始方式

Leopard 持久化 jdbc 示例

@Overridepublic boolean add(GameTask gameTask) {

InsertBuilder builder = new InsertBuilder("game_task", true);builder.setInt("game_task_id", gameTask.getGameTaskId());builder.setInt("frequency", gameTask.getFrequency());builder.setString("task", gameTask.getTask());builder.setBool("del", gameTask.isDel());return jdbc.insertForBoolean(builder);

}

Leopard 方式

public int update(AdminUser adminUser) {StringBuilder sql = new StringBuilder();sql.append("update ").append(TABLE).append(" set nick_name=?,

password=?,real_name=?,group_id=?, add_time=?,status=? where admin_id=?");Object[] args = new Object[] { adminUser.getNickName(),

adminUser.getPassword(), adminUser.getRealName(), adminUser.getGroupId(), adminUser.getAddTime(), adminUser.getStatus().getId(), adminUser.getAdminId()

};return getJdbcTemplate().update(sql.toString(), args);

}

Leopard 持久化 jdbc 示例

数据更新 Spring 原始方式

public boolean update(GameTask gameTask) {UpdateBuilder builder = new UpdateBuilder("game_task");builder.setString("task", gameTask.getTask());builder.setInt("frequency", gameTask.getFrequency());builder.where.setInt("game_task_id", gameTask.getGameTaskId());return jdbc.updateForBoolean(builder);

}

Leopard 方式

Leopard 持久化 jdbc 示例

@Overridepublic AdminUser get(String adminId) {

StringBuilder sql = new StringBuilder();sql.append("select * from ").append(TABLE).append

(" where admin_id=?");Object[] args = new Object[] { adminId };return getJdbcTemplate().query(sql.toString(), args, resultSetExtractor());

}

protected ResultSetExtractor<T> resultSetExtractor() {return new ResultSetExtractor<T>() {

@Overridepublic T extractData(ResultSet rs) throws SQLException, DataAccessException {

return rs.next() ? rsToObject(rs) : null;}

};}

Leopard 持久化 jdbc 示例

获取数据 Spring 原始方式

protected AdminUser rsToObject(ResultSet rs) throws SQLException {AdminUser adminUser = new AdminUser();adminUser.setAdminId(rs.getString("admin_id"));adminUser.setPassword(rs.getString("password"));adminUser.setGroupId(rs.getInt("group_id"));adminUser.setNickName(rs.getString("nick_name"));adminUser.setAddTime(rs.getTimestamp("add_time"));adminUser.setRealName(rs.getString("real_name"));adminUser.setStatus(AdminStatus.getStatus(rs.getInt("status")));return adminUser;

}

Leopard 持久化 jdbc 示例

每一个 model 都要写一遍这样的转换,好累。

Leopard 持久化 jdbc 示例

public GameTask get(Integer gameTaskId) {String sql = "SELECT * FROM game_task WHERE game_task_id=? AND del=0“;return jdbc.query(sql, GameTask.class, gameTaskId);

}

Leopard 方式

1500 万 /10/50 = 30 万 > MYSQL 4000/s = 忽略

public List<AdminUser> list(int start, int len) {StringBuilder sql = new StringBuilder();sql.append("select ").append(Columns).append(" from ").

append(TABLE).append(" where admin_id!=? limit ?,?");Object[] args = new Object[] { ConstantUtil.ADMIN_ACCOUNT, start, len };return getJdbcTemplate().query(sql.toString(), args, rowMapper());

}

public List<GameTask> listByGame(String gameId, int start, int size) {String sql = “SELECT * FROM game_task WHERE game_id=? AND del=0 LIMIT ?,?”;return jdbc.queryForList(sql, GameTask.class,param , start, size);

}

Leopard 持久化 jdbc 示例

Leopard 方式

获取数据 Spring 原始方式

API

Leopard 持久化 api

Leopard 持久化 Redis

简化 Redis 配置支持按 String(passport) 、 Long(yyuid) Hash 存储数据

与 Redis 命令一致的方法名Redis 持久化指令日志

指定时间自动备份 Redis 数据库

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance mlns:leopard=http://leopard.yy.com/schema/leopardxsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://leopard.yy.com/schema/leopard

http://leopard.yy.com/schema/leopard-0.1.xsd">

<leopard:redis id="indexRedisLog4jImpl" server="${index.redis.yyvip.game.yy.com}:6384" maxActive="128" timeout="30000" enableBackup="true“log="true"/>

</beans>

Leopard 持久化 Redis 配置

package com.duowan.yuepiao.dao.redis;@Repositorypublic class UserDaoRedisImpl implements UserDao {

@Resource(name = "indexRedisLog4jImpl")private Redis redis;@Overridepublic boolean add(User user) {

String key = RedisKey.getUser();String field = Long.toString(user.getYyuid());String value = Json.toJson(user);Long num = this.redis.hset(key, field, value);return NumberUtil.isNotNull(num);

}@Overridepublic User get(Long yyuid) {

String key = RedisKey.getUser();String field = yyuid.toString();String value = this.redis.hget(key, field);return Json.toObject(value, User.class);

}}

注入 redis 数据源

执行 hash set 操作

执行 hash get 操作

Leopard 持久化 Redis 示例

Leopard 持久化

与 Redis 命令一致的方法名

Leopard 持久化

<bean id="passportRedisHashImpl" class="com.duowan.leopard.data.redis.RedisHashImpl"

init-method="init" destroy-method="destroy" lazy-init="true" ><property name="hashType" value="long" /><property name="timeout" value="30000" /><property name="serverList"><list>

<value>${passport1.redis.uproxy.game.yy.com}:6351</value><value>${passport2.redis.uproxy.game.yy.com}:6352</value></list></property>

</bean>

Leopard 持久化

package com.duowan.uproxy.dao.redis;@Repositorypublic class PassportByYyuidDaoRedisImpl extends ContextImpl implements PassportByYyuidDao {

@Resource(name = "passportRedisHashImpl")private Redis redis;@Overridepublic boolean add(PassportYyuidDto passportYyuidDto) {

String passport = passportYyuidDto.getPassport();long yyuid = passportYyuidDto.getYyuid();String key = RedisKeyConstants.getYyuidKey(yyuid);this.redis.set(key, passport, RedisKeyConstants.YEAR_EXPIRED_TIME);return true;

}@Overridepublic PassportYyuidDto get(Long yyuid) {

String key = RedisKeyConstants.getYyuidKey(yyuid);String passport = this.redis.get(key);PassportYyuidDto passportYyuidDto = new PassportYyuidDto();passportYyuidDto.setPassport(passport);passportYyuidDto.setYyuid(yyuid);return passportYyuidDto;

}}

Leopard 持久化

Memcache,Memdb 等待您的挖掘……

Leopard 持久化 单元测试

package com.duowan.demo.dao.mysql;import org.junit.Test;import com.duowan.leopard.test.mock.Mock;import com.duowan.leopard.test.mock.MockTransactionalTests;public class UserDaoMysqlImplTest extends MockTransactionalTests{

protected UserDaoMysqlImpl userDaoMysqlImpl = Mock.dao(this, UserDaoMysqlImpl.class);

@Testpublic void add() {

Assert.dao(userDaoMysqlImpl).get(1);}@Testpublic void get() {

Assert.dao(userDaoMysqlImpl).get(1);}

}

与通用 API 完美的结合……

Leopard 持久化 单元测试

Leopard 对 Mock 、 Assert 进行了封装,并独创 Tson 简化单元测试代码

Leopard 持久化 Tson 示例

1 、简单对象{gameId:sxd,server:s1}

2 、 list 基本类型 list [1,2,3]对象类型 list[ gameId:sxd,server:s1; gameId:smxj,server:s2; gameId:zxy ]

3 、 mapTson.toMapObject(content,keyType, valueType);Tson.parseMap(content);只支持基本类型{1:a,2:b,3:c}

4 、时间Tson.toTime(“10ms”);支持单位: ms/s/m/h/DTson.parseDate(“2013-04-25”);支持 now, now+, now-

Leopard 持久化 Mock 示例

spy 测试对象ActivityPokerServiceImpl activityPokerService = Mock.spy(this, ActivityPokerServiceImpl.class);

mock 返回值返回 List<Integer>Mock.doReturn("[1,2,3]").when(articleService).listArticleId();

返回 List<String>Mock.doReturn("[a,b,c]").when(articleService).listUsername();

返回 List<Model>Mock.doReturn("[gameId:ddt;gameId:sxd,userCount:1]“)

.when(gameService).listAllGame();

返回 List<Entry<Integer, Date>>Mock.doReturn("[1,now;2,now]").when(this.outsideService).listMyGroups(“test");

verify 执行次数Mock.verify(pokerService).getById(pokerId); 默认一次Mock.verify(pokerService,2).getById(pokerId); 必须执行 2 次

Leopard 持久化 Mock 示例

代理方法 Assert( 程序会自动查找 service 第一个以 Dao 结尾的属性类型,作为 Dao)Assert.when(pokerServiceImpl).get(pokerId);

验证调用次数Assert.verify("indexActivityPokerService.indexPoker", 1).verify();

验证未实现方法Assert.noimpl(pokerMissionDaoRedisImpl).remove(1);

异常 assert, 异常类型只支持类名 equals 判断 .Assert.except(IllegalArgumentException.class).when(pokerServiceImpl).get(0);

Dao 测试 ( 标准接口 )Assert.dao(pokerMissionDaoRedisImpl).get(missionId);

Leopard 持久化 Assert 示例

Leopard 持久化 集成测试

package com.duowan.demo.dao.mysql;public class UserDaoMysqlImplIntegrationTest extends IntegrationTests{

@Autowiredprivate UserDao userDaoMysqlImpl;

@Testpublic void add() {

User user = new User();user.setUserId(1);user.setName("luxh");user.setEmail("lu_gg@126.com");user.setMarry(true);user.setSex(" 男 ");user.setAge(31);user.setAddress(" 广东珠海 ");userDaoMysqlImpl.add(user);

}@Testpublic void get() {

Json.print(userDaoMysqlImpl.get(1));}

}

Leopard Web 开发

Leopard Web 相关 Controller

package com.duowan.zhibo.web.controller;  @Controller@RequestMapping(value=GainController.DIR)public class GainController { 

public static final String DIR = "/gain“;@RequestMappingpublic JsonView canGain(long sessYyuid, String proxyIp){

boolean result = this.gainHandler.canGain(sessYyuid, proxyIp);Map<String, Object> map = new HashMap<String, Object>();map.put("status", result);return new JsonView(map);

}}

1.sessYyuid, proxyId 等特殊参数。

2. 请求路径: /gain/canGain.do, 默认用方法名来做请求路径。

3. 自定义 JsonView 视图 。

Leopard Web 相关 特殊参数

sessYyuidsessUsername (passport)

proxyIp

yygVer

requestUri

userAgent

sessUserId

cookieUsernamecookieLoginedUsername

cookieYyuidpageId

Leopard Web 相关 自定义视图

Leopard Web 相关 参数转换器

Boolean

long

Long

int

Integer

Date

Leopard Web 相关 异常处理机制

errorpage.xml<?xml version="1.0" encoding="UTF-8"?><errorpage xmlns="http://leopard.yy.com/schema/leopard"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://leopard.yy.com/schema/leopard http://leopard.yy.com/schema/leopard-0.1.xsd">

<error url="/money/salary.do“><exception type="StatusCodeException" statusCode="404" log="false"

/></error>

<error url="/" page="/error" /></errorpage>

根据 URL 自定义异常处理,并可以忽略已知异常的日志信息。

Leopard Web 相关 集成 udb 登录

package com.duowan.yyvip.handler.impl;

@Componentpublic class LoginHandlerImpl extends LoginHandler {

@Overridepublic List<String> getExcludeUris() {

List<String> list = new ArrayList<String>();list.add("/gameTask/list.do");return list;

}}

Leopard Web 相关 分布式 session

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:leopard="http://leopard.yy.com/schema/leopard"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

http://leopard.yy.com/schema/leopard http://leopard.yy.com/schema/leopard-0.1.xsd">

<leopard:memcache id="memcache" name="session"/>

</beans>

<leopard:config distributedSession="false"/>

Leopard 其他功能

Leopard Web 相关 定时器

郁闷,定时器还没执行完,又启动了一个……?

Leopard Web 相关 定时器

package com.duowan.zhibo.timer;@Componentpublic class GameScheduleTimer extends AbstractTimer{

protected Log logger = LogFactory.getLog(getClass());@Autowiredprivate GameScheduleHandler gameScheduleHandler;

@Overridepublic boolean isEnabled() {

return Config.isEnableTimer();}@Overridepublic Period getPeriod() {

return new PerDayPeriod(11, 20);}

@Overridepublic void start() {

Date now = new Date();this.gameScheduleHandler.activate(now);

}}

Leopard Web 相关 已实现的定器类型

1. PerDayPeriod(int hour, int minute)

2. PerMinutePeriod(int minute)

3. PerHourPeriod(int minute)

4. SecondPeriod(int second)

5. WeekPeriod(int weekday, int hour, int minute)

6. OncePeriod()

Leopard Web 相关 防并发限制

package com.duowan.yyvip.handler.impl;@Componentpublic class LoginHandlerImpl extends LoginHandler {

@Overridepublic List<String> getConnectionLimitIncludeUris()

{List<String> urlList = new ArrayList();urlList.add("/test/test.do");return urlList;

}}

<leopard:connection-limit seconds="1" redis-ref="indexRedisLog4jImpl" />

Leopard Web 相关 Jvm 数据同步

package com.duowan.yyvip.service.impl; @Servicepublic class GameTaskXmlServiceImpl implements GameTaskXmlService , IPubSub{ 

public String xml = "“;@Overridepublic boolean update(String xml) {

this.xml = xml;return this.publish(xml); // 这里发布

} @Overridepublic String get() {

return xml;} @Overridepublic boolean publish(String message) {

return Publisher.publish(this, message);} @Overridepublic void subscribe(String message, boolean isMySelf) {

if(!isMySelf){xml = message;

}}

Leopard Web 相关 防 Xss 攻击

protected void doFilter2(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;UserinfoUtil.checkXSS(request);UserinfoWrapper httpRequestWraper = new chain.doFilter(httpRequestWraper, response);

}

Leopard Web 相关 打开性能监控

<leopard:config performance=“true"/>

http://zhibo.game.duowan.com/monitor/performance.do

Leopard Web 相关 权限控制

<import resource="permission/applicationContext.xml" /><import resource="permission/usemysql.xml" />

CREATE TABLE `permission` ( `uri` varchar(100) NOT NULL DEFAULT '', `ip` varchar(23) NOT NULL DEFAULT '', `username` varchar(50) NOT NULL DEFAULT '', `posttime` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', `content` text, PRIMARY KEY (`uri`,`ip`), KEY `ip_idx` (`ip`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into permission values('/webservice/', '127.0.0.1', 'dw_tanhaichao',now(),' 本机 ');

Leopard 是一个 Full-stack 框架

Leopard 注重约定优于配置Leopard 更注重减少重复劳动……

Leopard 总结

亲们,还等什么,马上行动吧!

技术支持:谭海潮、卢轩华、李儇

END Thank you