# Summer **Repository Path**: mingshashan/Summer ## Basic Information - **Project Name**: Summer - **Description**: 这是一个支持分布式和集群的java游戏服务器框架,可用于开发棋牌、回合制等游戏。基于netty实现高性能通讯,支持tcp、http、websocket等协议。支持消息加解密、攻击拦截、黑白名单机制。封装了redis缓存、mysql数据库的连接与使用。轻量级,便于上手。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2020-03-20 - **Last Updated**: 2023-05-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Summer 这是一个支持分布式和集群的java游戏服务器框架,可用于开发棋牌、回合制等游戏。基于netty实现高性能通讯,支持tcp、http、websocket等协议。支持消息加解密、攻击拦截、黑白名单机制。封装了redis缓存、mysql数据库的连接与使用。轻量级,便于上手。 ## 目录 - 前言 - 环境介绍与安装说明 - 快捷上手 - 创建项目 - 项目结构 - 运行项目 - 框架介绍 - 组件介绍 - 注解介绍 - 核心方法介绍 - 异常介绍 - 协议介绍 - Web介绍 - 运行机制 - 其他介绍 ## 前言 ## 更新 ### 2019.02.20 1. 新增Summer.getServerEventLoopGroup,用于获取服务器业务线程池 2. 新增Summer.getSessionQueueSize,用于获取会话队列长度 3. 优化SessionQueue、SingleQueue队列,不再分配新线程,将以队列的形式逐个提交到服务器业务线程中(在此特别感谢一位大哥的支持与协助) ### 2019.02.17 1. ClientRemote类,新增rsyncRemote方法,调用接口超时将自动重试直到成功为止。 2. ClientRemote类,新增getServerName方法,用于获取连接其他服务器的节点名称。 3. 新增Summer.getRemoteInvokeObjectWithRetry、Summer.getRandomRemoteInvokeObjectWithRetry,用于获取连接其他服务器的远程调用接口代理对象,超时将自动重试直到成功为止。 ### 2019.01.28 1. 修复了,服务器之间远程调用可能出现丢包的问题,原因是消息id不能正确的递增,解决办法是修改了消息id的判断。 2. 当作为web服务器时,若接口返回的类型不是WebView,则将返数据序列化成Json,并返回TextView。 ## 环境介绍与安装说明 JDK 1.8 以上
MySql 5.7 (仅供参考)
Redis 5.0 (仅供参考)
IDE: eclipse (仅供参考)
将SummerServer库添加至UserLibraries #### SummerServer库 asm-6.2.jar
cglib-3.2.7.jar
commons-dbutils-1.7.jar
commons-pool2-2.4.2.jar
druid-1.1.10.jar
fastjson-1.2.47.jar
freemarker-2.3.23.jar
javassist-3.22.0-GA.jar
jedis-2.9.0.jar
log4j-1.2.17.jar
mysql-connector-java-5.1.20-bin.jar
netty-all-4.1.23.Final.jar
quartz-2.3.0.jar
slf4j-api-1.7.25.jar
slf4j-log4j12-1.7.25.jar
## 快捷上手 ### 创建项目 1. 创建普通的java project项目 2. 为当前项目添加SummerServer库 ### 项目结构 - src - lib - config - Template #Web项目 - WebContent #Web项目 #### src ##### 包结构 - com.test.summerDemo - bean #实体 - constant #常量 - dao #数据库操作 - service #业务逻辑 - event #事件触发器 - handler #服务器会话回调 - push #推送接口 - remote #远程接口 - task #定时任务 - exception #异常信息 - manager #对象管理 - util #工具 - SummerDemoApp.java #启动类 ##### 启动类 ```java package com.test.summerDemo; import com.swingfrog.summer.app.Summer; import com.swingfrog.summer.app.SummerApp; public class SummerDemoApp implements SummerApp { @Override public void init() { } @Override public void start() { } @Override public void stop() { } public static void main(String[] args) throws Exception { Summer.hot(new SummerDemoApp()); } } ``` ##### 其余的组件将在下文逐一介绍 #### lib 引用外部jar包请放在此目录下,并添加引用。
引用Summer.jar,此jar包依赖SummerServer库。 #### config - db.properties #数据库配置文件 - redis.properties #缓存配置文件 - log.properties #日志配置文件 - task.properties #任务配置文件 - server.properties #服务器配置文件 ##### db.properties (druid的配置文件) ```properties driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull username=root password=123456 filters=stat initialSize=2 maxActive=300 maxWait=60000 timeBetweenEvictionRunsMillis=60000 minEvictableIdleTimeMillis=300000 validationQuery=SELECT 1 testWhileIdle=true testOnBorrow=false testOnReturn=false poolPreparedStatements=false maxPoolPreparedStatementPerConnectionSize=200 ``` ##### redis.properties (jedis配置文件) ```properties url=127.0.0.1 port=6379 timeout=3000 password=123456 blockWhenExhausted=true evictionPolicyClassName=org.apache.commons.pool2.impl.DefaultEvictionPolicy jmxEnabled=true maxIdle=8 maxTotal=200 maxWaitMillis=100000 testOnBorrow=true ``` ##### log.properties (log4j配置文件) ```properties log4j.rootLogger=DEBUG,C log4j.logger.org.quartz=OFF log4j.logger.com.alibaba.druid=DEBUG log4j.logger.io.netty=OFF log4j.appender.C=org.apache.log4j.ConsoleAppender log4j.appender.C.Target=System.out log4j.appender.C.layout=com.swingfrog.summer.log.ColorPatternLayout log4j.appender.C.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss\:SSS} [%t] %p [] %m %n log4j.appender.F=org.apache.log4j.DailyRollingFileAppender log4j.appender.F.File=log/container log4j.appender.F.Append=true log4j.appender.F.DatePattern='.'yyyy-MM-dd log4j.appender.F.layout=com.swingfrog.summer.log.ColorPatternLayout log4j.appender.F.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss\:SSS} [%t] %p [] %m %n ``` ##### task.properties (quartz配置文件) ```properties org.quartz.scheduler.instanceName=Task org.quartz.scheduler.rmi.export=false org.quartz.scheduler.rmi.proxy=false org.quartz.scheduler.wrapJobExecutionInUserTransaction=false org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=10 org.quartz.threadPool.threadPriority=5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true org.quartz.jobStore.misfireThreshold=60000 org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore ``` ##### server.properties (服务器配置文件) ```properties #服务器集群名称 server.cluster=Gate #服务器节点名称 server.serverName=gate_s1 #绑定地址 server.address=127.0.0.1 #绑定端口 server.port=8828 #通讯协议 server.protocol=Http #消息编码 server.charset=UTF-8 #消息密码 server.password=123456 #侦听线程数 server.bossThread=0 #读写线程数 server.workerThread=0 #业务线程数 server.eventThread=0 #消息最大长度 单位字节 server.msgLength=1024000 #心跳时间 单位秒 server.heartSec=40 #请求的间隔时间 server.coldDownMs=10 #是否开启连接白名单 server.allowAddressEnable=true #白名单允许连接的地址 server.allowAddressList=127.0.0.1,127.0.0.2 #连接其他服务器的列表 server.clientList=account_s1,account_s2 #其他服务器的集群名称 client.account_s1.cluster=Account #其他服务器的节点名称 client.account_s1.serverName=account_s1 #连接地址 client.account_s1.address=127.0.0.1 #连接端口 client.account_s1.port=8828 #通讯协议 client.account_s1.protocol=StringLine #消息编码 client.account_s1.charset=UTF-8 #消息密码 client.account_s1.password=123456 #读写线程数 client.account_s1.workerThread=0 #业务线程数 client.account_s1.eventThread=0 #消息最大长度 单位字节 client.account_s1.msgLength=1024 #心跳时间 单位秒 client.account_s1.heartSec=20 #断线重连间隔时间 单位毫秒 client.account_s1.reconnectMs=100 #远程调用超时时间 单位毫秒 client.account_s1.syncRemoteTimeOutMs=5000 #连接数 client.account_s1.connectNum=1 client.account_s2.cluster=Account client.account_s2.serverName=account_s2 client.account_s2.address=127.0.0.1 client.account_s2.port=8828 client.account_s2.protocol=StringLine client.account_s2.charset=UTF-8 client.account_s2.password=123456 client.account_s2.workerThread=0 client.account_s2.eventThread=0 client.account_s2.msgLength=1024 client.account_s2.heartSec=20 client.account_s2.reconnectMs=100 client.account_s2.syncRemoteTimeOutMs=5000 client.account_s2.connectNum=1 ``` ### 运行项目 #### 开发环境 在eclipse中可直接运行或调试,启动类为SummerDemoApp.class #### 生产环境 ##### 打包 在Options中勾选Add directory entries。
注意,不要导出可运行的jar文件,因为会把lib中引用的jar和引用的库打包进jar中,造成jar体积巨大。 ##### 项目结构 - SummerDemo - SummerDemo.jar - lib - config - Template - WebContent - SummerRuntime.jar ##### 使用SummerRuntime.jar运行 java -jar SummerRuntime.jar SummerDemo/SummerDemo.jar com.test.summerDemo.SummerDemoApp ## 框架介绍 ### 组件介绍 SummerApp由辅助组件和主要组件组成,其中bean、constant、manager、util、exception为辅助组件,dao、service、event、handler、push、remote、task、app为主要组件。 #### bean javabean、数据表的实体映射 #### constant 常量声明 #### manager 对象管理,使用时在类上方使用注解@Bean ```java @Bean public class LoginManager { private ConcurrentHashMap accountIdMap = new ConcurrentHashMap<>(); private ConcurrentHashMap sessionContextMap = new ConcurrentHashMap<>(); //省略... } ``` #### util 工具类 #### exception 异常信息声明 #### dao 数据库操作,类需继承BaseDao并使用注解@Dao ```java @Dao public class AccountDao extends BaseDao { public Account getById(int id) { return getBean("select * from t_account where id = ?", id); } } ``` ```java public abstract class BaseDao { protected int update(String sql, Object... args){} protected Long insertAndGetGeneratedKeys(String sql, Object... args){} protected T getBean(String sql, Object... args) {} protected List listBean(String sql, Object... args) {} protected E getValue(String sql, Object... args) {} protected List listValue(String sql, Object... args) {} protected Map getMap(String sql, Object... args) {} protected List> listMap(String sql, Object... args) {} protected E getBeanByClass(String sql, Class clazz, Object... args) {} protected List listBeanByClass(String sql, Class clazz, Object... args) {} } ``` #### service 业务处理,使用时在类上方使用注解@Service
```java @Service public class AccountService { @Autowired private AccountDao accountDao; public Account getAccountById(int accountId) { return accountDao.getById(accountId); } } ``` #### event 事件处理器,使用时在类上方使用注解@EventHandler
在对应方法上方使用注解@BindEvent,参数为监听的事件的名称。也可使用@BindEvent(value = "事件名称", index = 1),index表示同名事件处理器的先后顺序,index小到大,顺序先到后。
如果该方法的返回值不为viod、null,则表示对该事件进行拦截,因此后面的事件处理器便不会收到通知。 ```java @EventHandler public class FriendEvent { @BindEvent("登录事件") public void noticeFriend(int accountId) {} } ``` #### handler 服务器会话回调,类需实现SessionHandler并使用注解@ServerHandler
此组件主要用于网关服务器,可对用户的连接和请求进行拦截或其他处理 ```java @ServerHandler public class LoginHandler implements SessionHandler { //是否允许该会话连接服务器 此处可进行黑名单拦截或白名单放行 @Override public boolean accpet(SessionContext sctx) { return true; } //会话连接成功 @Override public void added(SessionContext sctx) { } //会话心跳超时 @Override public void heartTimeOut(SessionContext sctx) { } //会话发来的消息长度大于配置 @Override public void lengthTooLongMsg(SessionContext sctx) { } //是否接收会话发来的消息 @Override public boolean receive(SessionContext sctx, SessionRequest request) { return true; } //会话断开连接 @Override public void removed(SessionContext sctx) { } //会话发送重复消息 @Override public void repetitionMsg(SessionContext sctx) { } //会话发送消息次数间隔小于配置 @Override public void sendTooFastMsg(SessionContext sctx) { } //会话发来的消息无法解析 @Override public void unableParseMsg(SessionContext sctx) { } } ``` #### push 推送接口,使用时在类上方使用注解@Push
用于将消息推送给连接本服务器的其他服务器或客户端 ```java @Push public class DataPush { public void pushDataToAll(DataPushMsg msg) { Summer.getServerPush().asyncPushToAll(msg.getRemote(), msg.getMethod(), msg.getData()); } } ``` #### remote 远程调用接口,使用时在类上方使用注解@Remote
SessionContext为调用此接口的会话,此参数可省略。
除了标记@Optional的参数外,皆为必填参数,如有遗漏会抛出异常。 ```java @Remote public class AccountRemote { @Autowired private AccountService accountService; public Account getAccount(SessionContext sctx, int accountId, @Optional remark) { return accountService.getAccountById(accountId); } } ``` #### task 定时任务,使用时在类上方使用注解@Task
在对应的方法上方添加注解@CronTask、@IntervalTask,即表示该方法为一个任务。
@CronTask("cron 表达式")当时间满足cron表达式时执行该方法
@IntervalTask(1000)每隔1000毫秒执行该方法,或使用@IntervalTask(value = 1000, delay = 2000)2000毫秒后执行该方法,然后每隔1000毫秒执行该方法。 ```java @Task public class StatTask { @CronTask("0 0/5 * * * ? ") public void onlineStatTask() { } @IntervalTask(1000) public void updateXX() { } @IntervalTask(value = 1000, delay = 2000) public void waitAndUpdate() { } } ``` #### app app启动类,此类需实现SummerApp且添加程序入口main方法,并在main方法中调用启动框架。
Summer.hot会在后面提到。 ```java public class SupmersGateApp implements SummerApp { private static final Logger log = LoggerFactory.getLogger(SupmersGateApp.class); //框架初始化后回调 @Override public void init() { log.info("gate init"); } //框架启动后回调 @Override public void start() { log.info("gate start"); } //框架停止后回调 @Override public void stop() { log.info("gate stop"); } public static void main(String[] args) throws Exception { Summer.hot(new SupmersGateApp()); } } ``` #### 组件之间的调用关系 remote 可调用service、util、manager、constant、bean、exception
push 可调用service、util、manager、constant、bean
handler 可调用service、util、manager、constant、bean
event 可调用service、util、manager、constant、bean
task 可调用service、util、manager、constant、bean、exception
service 可调用dao、service、util、manager、constant、bean、exception
util 可调用util、manager、constant、bean
manager 可调用util、manager、constant、bean
constant 无
bean 无

remote 由远程服务器或客户端调用
push 由远程服务器推送调用
handler 由框架根据会话信息调用
event 由事件驱动器调用
task 由任务处理器调用 ### 注解介绍 注解主要分为两大类,组件类与辅助类。
#### 组件类注解 @Bean、@Dao、@Service、@Remote、@Push、@Task、@ServerHandler、@EventHandler
此类注解只用于类
使用此注解的类,在框架启动时,会自动扫描进容器并实例化常驻于内存中。 #### 辅助类注解 @Autowired、@Synchronized、@SingleQueue、@SessionQueue、@Optional、@Transaction、@CronTask、@IntervalTask、@BindEvent
此类注解只用于字段、方法、参数 #### @Bean 声明此类为容器中普通组件(manager、other)。 #### @Dao 声明此类为数据库操作(dao)。 #### @Service 声明此类为业务处理(service)。 #### @Remote 声明此类为远程接口(remote)。 #### @Push 声明此类为推送接口(push)。 #### @Task 声明此类为定时任务(task)。 #### @ServerHandler 声明此类为服务器会话回调(handler)。 #### @EventHandler 声明此类为事件处理器(event)。 #### @Autowired 在使用@Service、@Remote、@Push、@Task、@ServerHandler、@EventHandler这些注解的类中,其字段如果使用此注解,即可实现自动注入,注入的对象由容器提供。
组件中只有@Bean、@Dao、@Service支持被注入。 ```java @Remote public class AccountRemote { @Autowired private AccountService accountService; @Autowired private StatService statService; @Autowired private ItemService itemService; @Autowired private DanService danService; @Autowired private PushManager pushManager; } ``` #### @Synchronized 在使用@Service、@Remote、@Task、@EventHandler这些注解的类中,其方法如果使用此注解,即可为该方法上分布式锁。当该方法被调用时,会尝试获取锁,一直等到获取成功,执行完方法或抛异常会自动释放锁。
此锁适用于多服务器同步。 ```java @Remote public class ShopRemote { @Synchronized("购物锁") public void buyGoods(int accountId, int goods) {} } ``` #### @SingleQueue 在使用@Remote注解的类中,其方法如果使用此注解,在多个线程调用此方法是,会排进指定的队列中,依次完成调用。 ```java @Remote public class StatRemote { @SingleQueue("队列名称") public void peopleOnline(int accountId) {} } ``` #### @SessionQueue 在使用@Remote注解的类中,其方法如果使用此注解,在多个线程调用此方法是,会排进当前请求者的会话队列中,依次完成调用。 ```java @Remote public class ItemRemote { @SessionQueue public void listItem(int accountId) {} } ``` #### @Optional 在使用@Remote注解的类中,其方法参数如果使用此注解,即视为选填参数。 ```java @Remote public class AccountRemote { public Account getAccount(SessionContext sctx, int accountId, @Optional remark) {} } ``` #### @Transaction 在使用@Remote、@Task、@EventHandler这些注解的类中,其方法如果使用此注解,即可开启mysql事务管理,方法执行完则提交事务,如抛出异常则回滚事务。 ```java @Remote public class FriendRemote { @Transaction public void addFriend(int accountId, String name) {} } ``` #### @CronTask 在使用@Task注解的类中,其方法参数如果使用此注解,即视为定时任务。
@CronTask("cron 表达式") ```java @Task public class StatTask { @CronTask("0 0/5 * * * ? ") public void onlineStatTask() {} } ``` #### @IntervalTask 在使用@Task注解的类中,其方法参数如果使用此注解,即视为间隔任务。
@IntervalTask(1000) 立即执行并每1000毫秒再执行。
@IntervalTask(value = 1000, delay = 2000) 等待2000毫秒执行并每1000毫秒再执行。
```java @Task public class StatTask { @IntervalTask(1000) public void updateXX() {} @IntervalTask(value = 1000, delay = 2000) public void waitAndUpdate() {} } ``` #### @BindEvent 在使用@EventHandler注解的类中,其方法参数如果使用此注解,即为该方法绑定了相应的事件,当有指定的事件发出时,事件驱动就会调用该方法。
@BindEvent("事件名称")
@BindEvent(value = "事件名称", index = 1) index表示同名事件处理器的先后顺序, index小到大,顺序先到后。 ```java @EventHandler public class FriendEvent { @BindEvent("登录事件") public void noticeFriendOnline(int accountId) {} @BindEvent(value = "登出事件", index = 1) public void noticeFriendoffline(int accountId) {} } ``` ### 核心方法介绍 核心方法可在任意地方使用。 #### Summer.hot Summer框架启动方法 ```java public static void hot(SummerApp app) throws Exception {} public static void hot(SummerApp app, String projectPackage) throws Exception {} public static void hot(SummerApp app, String projectPackage, String libPath, String serverProperties, String logProperties, String redisProperties, String dbProperties, String taskProperties) throws Exception {} ``` #### Summer.sync 分布式锁 ```java public static void sync(String key, Runnable runnable) {} ``` #### Summer.execute 队列处理 ```java public static void execute(Object key, Runnable runnable) {} ``` #### Summer.addComponent 添加组件到容器 ```java public static void addComponent(Object obj) {} ``` #### Summer.removeComponent 从容器中移除组件 ```java public static void removeComponent(Object obj) {} ``` #### Summer.getComponent 从容器中获取组件 ```java public static T getComponent(Class clazz) {} ``` #### Summer.getProxyInstance 创建代理对象 ```java public static T getProxyInstance(Object target, ProxyMethodInterceptor interceptor) {} ``` ```java public interface ProxyMethodInterceptor { public Object intercept(Object obj, Method method, Object[] args) throws Throwable; } ``` #### Summer.autowired 组件注入,为目标对象中使用@Autowired注解的字段,进行对象注入。 ```java public static void autowired(Object obj) {} ``` #### Summer.getRedisSource 获取Redis源,可用于操作Redis。 ```java public static RedisSource getRedisSource() {} ``` #### Summer.getIntervalTask 创建间隔任务 ```java public static TaskTrigger getIntervalTask(long interval, long delay, String taskName, TaskJob taskJob) {} ``` #### Summer.getCronTask 创建定时任务 ```java public static TaskTrigger getCronTask(String cron, String taskName, TaskJob taskJob) {} ``` #### Summer.startTask 开始任务 ```java public static void startTask(TaskTrigger taskTrigger) {} ``` #### Summer.stopTask 停止任务 ```java public static void stopTask(TaskTrigger taskTrigger) {} ``` #### Summer.getClientRemote 通过集群名称和服务器节点名称获取连接其他服务器的远程调用接口对象 ```java public static ClientRemote getClientRemote(String cluster, String name) {} ``` ```java public class ClientRemote { //异步调用远程接口 public void asyncRemote(String remote, String method, Object data, RemoteCallback remoteCallback) {} //同步调用远程接口 (如果等待时间超出配置,则抛出异常) public T syncRemote(String remote, String method, Object data, Type type) {} } ``` #### Summer.getRandomClientRemote 通过集群名称,随机获取连接其他服务器的远程调用接口对象 ```java public static ClientRemote getRandomClientRemote(String cluster) {} ``` #### Summer.getRemoteInvokeObject 通过集群名称和服务器节点名称获取连接其他服务器的远程调用接口代理对象 ```java public static T getRemoteInvokeObject(String cluster, String name, Class clazz) {} ``` #### Summer.getRemoteInvokeObjectWithRetry 通过集群名称和服务器节点名称获取连接其他服务器的远程调用接口代理对象,超时将自动重试直到成功为止 ```java public static T getRemoteInvokeObjectWithRetry(String cluster, String name, Class clazz) {} ``` #### Summer.getRandomRemoteInvokeObject 通过集群名称,随机获取连接其他服务器的远程调用接口代理对象 ```java public static T getRandomRemoteInvokeObject(String cluster, Class clazz) {} ``` #### Summer.getRandomRemoteInvokeObjectWithRetry 通过集群名称,随机获取连接其他服务器的远程调用接口代理对象,超时将自动重试直到成功为止 ```java public static T getRandomRemoteInvokeObjectWithRetry(String cluster, Class clazz) {} ``` ##### 账号服务器 ```java @Remote public class FriendRemote { @Autowired private FriendService friendService; @Transaction public void addFriend(int accountId, String name) { this.friendService.addFriend(accountId, name); } } ``` ```java public class AccountServerRemote { public static FriendRemote getFriendRemote() { return Summer.getRandomRemoteInvokeObject(ClusterConst.ACCOUNT, FriendRemote.class); } } ``` ##### 网关服务器 将账号服务器的jar包引入网关服务器中,即可像调用本地方法一样调用远程接口。 ```java @Remote public class FriendRemote { @Autowired private LoginManager loginManager; public void addFriend(SessionContext sctx, String name) { int accountId = loginManager.getAccountId(sctx); AccountServerRemote.getFriendRemote().addFriend(accountId, name); } } ``` #### Summer.getServerPush 获取服务器推送接口对象 ```java public static ServerPush getServerPush() {} ``` ```java public class ServerPush { //异步推送至该集群内所有服务器 public void asyncPushToClusterAllServer(String cluster, String remote, String method, Object data) {} //同步推送至该集群内所有服务器 public void syncPushToClusterAllServer(String cluster, String remote, String method, Object data) {} //异步推送至该集群内随机一台服务器 public void asyncPushToClusterRandomServer(String cluster, String remote, String method, Object data) {} //同步推送至该集群内随机一台服务器 public void syncPushToClusterRandomServer(String cluster, String remote, String method, Object data) {} //异步推送至该集群中指定的服务器 public void asyncPushToClusterThisServer(String cluster, String serverName, String remote, String method, Object data) {} //同步推送至该集群中指定的服务器 public void syncPushToClusterThisServer(String cluster, String serverName, String remote, String method, Object data) {} //异步推送至该会话 public void asyncPushToSessionContext(SessionContext sessionContext, String remote, String method, Object data) {} //同步推送至该会话 public void syncPushToSessionContext(SessionContext sessionContext, String remote, String method, Object data) {} //异步推送至部分会话 public void asyncPushToSessionContexts(List sessionContexts, String remote, String method, Object data) {} //同步推送至部分会话 public void syncPushToSessionContexts(List sessionContexts, String remote, String method, Object data) {} //异步推送至所有会话 public void asyncPushToAll(String remote, String method, Object data) {} //同步推送至所有会话 public void syncPushToAll(String remote, String method, Object data) {} } ``` #### Summer.closeSession 关闭会话 ```java public static void closeSession(SessionContext sctx) {} ``` #### Summer.getServerEventLoopGroup 获取服务器业务线程池 ```java public static EventLoopGroup getServerEventLoopGroup() {} ``` #### Summer.getSessionQueueSize 获取会话队列大小 ```java public static int getSessionQueueSize(SessionContext sctx){} ``` #### Summer.createCodeException 创建错误码异常对象 ```java public static CodeException createCodeException(long code, String msg) {} public static CodeException createCodeException(CodeMsg msg, Object ...args) {} ``` #### Summer.createCodeMsg 创建错误码消息 ```java public static CodeMsg createCodeMsg(long code, String msg) { ``` #### Summer.getCluster 获取集群名称 ```java public static String getCluster() {} ``` #### Summer.getServerName 获取服务器节点名称 ```java public static String getServerName() {} ``` #### Summer.syncDispatch 同步发送消息事件 ```java public static void syncDispatch(String eventName, Object ...args) {} ``` #### Summer.asyncDispatch 异步发送消息事件 ```java public static void asyncDispatch(String eventName, Object ...args) {} ``` #### Summer.getWeb 获取Web相关接口 ```java public static WebMgr getWeb() {} ``` ### 异常介绍 #### Error Code 100 调用异常 invoke error
当调用远程接口出现异常并且非自定义ErrorCode时,就会抛出此异常。 #### Error Code 101 远程接口不存在 remote not exist #### Error Code 102 远程方法不存在 method not exist #### Error Code 103 参数错误 parameter error #### 自定义 Error Code ##### 异常声明 ```java public class AccountException { /**金币不足*/ public static final CodeMsg GOLD_NOT_ENOUGH = Summer.createCodeMsg(101005, "gold not enough, accountId[%s] own[%s] need[%s]"); } ``` ##### 异常使用 建议只在remote和service中使用 ```java @Service public class AccountService { @Autowired private AccountDao accountDao; public int gainGold(int accountId, int gainGold) { int ownGold = accountDao.getGoldByIdForUpdate(accountId); int gold = ownGold + gainGold; if (gold < 0) { throw Summer.createCodeException(AccountException.GOLD_NOT_ENOUGH, accountId, ownGold, gainGold); } else if (gold > AccountConst.GOLD_MAX) { gold = AccountConst.GOLD_MAX; } accountDao.updateGold(accountId, gold); return gold; } } ``` ### 协议介绍 #### 消息定义 ##### 请求消息 ```json {"id": 0, "remote": null, "method": null, "data": {}} ``` id 由客户端不断递增,由1开始
remote 远程接口 -> 类名
method 远程方法 -> 方法名
data 数据 -> 方法参数名与值
##### 响应消息 ```json {"code": 0, "id": 0, "remote": null, "method": null, "data": null, "time": 0} ``` code 错误码,为0标识无异常
id 与客户端请求消息的id一致
remote 请求的远程接口
method 请求的远程方法
data 返回的数据
time 时间戳
##### 推送消息 ```json {"code": 0, "id": 0, "remote": null, "method": null, "data": null, "time": 0} ``` code 为0
id 为0,可根据id是否为0来判断是否是推送消息
remote 推送接口
method 推送方法
data 推送的数据
time 时间戳
#### StringLine协议 本协议支持加解密,支持服务器之间使用。消息格式为字符串,在字符串末尾加入\r\n,因此通过判断分隔符\r\n来区分消息。 ```properties #通讯协议 server.protocol=StringLine ``` #### WebSocket协议 本协议支持加解密,基于WebSocket协议。消息格式为二进制,数据包分为包头和包体,包头占四个字节,用来表示包体的长度。 ```properties #通讯协议 server.protocol=WebSocket ``` #### LengthField协议 本协议支持加解密,支持服务器之间使用。消息格式为二进制,数据包分为包头和包体,包头占四个字节,用来表示包体的长度。 ```properties #通讯协议 server.protocol=LengthField ``` #### Http协议 本协议不支持加解密,基于Http协议。 ```java @Remote public class TestRemote { public void test(String msg) { } } ``` ``` //地址:端口/远程接口_远程方法?请求数据 http://127.0.0.1:8080/TestRemote_test?msg=hello ``` ```properties #通讯协议 server.protocol=Http ``` #### 消息加解密算法 ##### WebSoccket与LengthField ```java byte[] bytes = new byte[0]; String pass = "123456"; //密码由配置文件配置 int index = bytes.length % 10; for (int i = 0; i < bytes.length; i++) { if (index >= pass.length) index = 0; int res = bytes[i] ^ pass[index]; bytes[i] = (byte)res; index++; } ``` ##### StringLine ```java byte[] bytes = new byte[0]; String pass = "123456"; //密码由配置文件配置 int index = bytes.length % 10; for (int i = 0; i < bytes.length; i++) { if (index >= pass.length) index = 0; int res = bytes[i] ^ pass[index]; if (res != 10 && res != 13) bytes[i] = (byte)res; index++; } ``` ### Web介绍 #### Web配置管理 ```java public class WebMgr { //重新加载模板 public void reloadTemplate() {} //获取模板 public Template getTemplate(String templateName) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {} //获取Web内容文件路径 public String getWebContentPath() {} //设置Web内容文件路径 public void setWebContentPath(String webContentPath) {} //获取模板文件路径 public String getTemplatePath() {} //设置模板文件路径 public void setTemplatePath(String templatePath) {} //获取内部视图渲染工厂 public InteriorViewFactory getInteriorViewFactory() {} //设置内部视图渲染工厂 public void setInteriorViewFactory(InteriorViewFactory interiorViewFactory) {} //获取主页路径 public String getIndex() {} //设置主页路径 public void setIndex(String index) {} //获取图标路径 public String getFavicon() {} //设置图标路径 public void setFavicon(String favicon) {} } ``` ##### 内部视图渲染工厂 如需自定义空白视图或错误视图,只需继承此类覆盖相应的方法。 ```java public class InteriorViewFactory { //空白视图 public BlankView createBlankView() {} //错误视图 public ErrorView createErrorView(int status, long code, String msg) {} //错误视图 public ErrorView createErrorView(int status, String msg) {} } ``` #### WebView视图渲染 ##### TextView 文字视图 ```java new TextView(String text); ``` ##### JSONView JSON视图 ```java new JSONView(JSON json); ``` ##### FileView 文件视图 ```java new FileView(String fileName); ``` ##### ModelView 模型视图 ```java ModelView model = new ModelView(String view); model.put(String key, Object value); ``` view 模板地址
key 键
value 值 ##### BlankView 空白视图 ```java new BlankView(); ``` ##### ErrorView 错误视图 ```java new ErrorView(int status, long code, String msg); new ErrorView(int status, String msg); ``` status Http状态
code 错误码
msg 错误消息 #### Web异常介绍 ##### Error Code 201 没有Web视图 not web view #### 数据提交 ```java @Remote public class UserRemote { public JSONView add(String name, Integer age, @Optional remark) { JSONObject json = new JSONObject(); json.put("flag", true); return new JSONView(json); } } ``` ##### get ``` http://127.0.0.1:8080/UserRemote_add?name=toke&age=22 //remark为选填 ``` ##### post ```html
; ; ;
``` #### 上传文件 ```java @Remote public class FileRemote { public void upload(WebFileUpload photo) { photo.saveToFile("photos/"); } } ``` ```html
;
``` ```java public class WebFileUpload { //获取文件名 public String getFileName() {} //获取数据 public ByteBuf getByteBuf() {} //保存到指定路径 public void saveToFile(String path) throws IOException {} } ``` ### 运行机制 通过Summer.hot启动框架。
初始化 -> 启动 -> 运行时 -> 停止
停止需要外部进行操作 kill -2 pid、kill -15 pid #### 初始化 1. 加载jar包 2. 加载server配置 3. 加载redis配置 4. 加载数据库配置 5. 加载任务配置 6. 扫描组件类注解,实例化并添加进容器 7. 服务器管理初始化 8. 客户端管理初始化 (连接其他服务器) 9. 事件驱动初始化 10. 组件注入对象 11. service、remote、task生成代理对象 #### 启动 1. 服务器启动 2. 客户端连接 (连接其他服务器) 3. 任务启动 #### 运行时 ##### 服务器管理 与会话保存心跳联系,心跳超时会通过handler通知。 ##### 客户端管理 (连接其他服务器) 与其他服务器保存心跳联系,心跳超时后断线会自动重连。 ##### 业务触发 两种方式触发业务处理,主动与被动。
主动,通过外部调用远程接口remote或会话的行为触发。
被动,通过内部任务处理器执行task触发。 #### 停止 停止一切 ### 其他介绍 #### 负载均衡 随机远程调用和随机推送都是通过轮询实现 ```java public Client getClientWithNext() { int size = clientGroupList.size(); if (size > 0) { if (size == 1) { return clientGroupList.get(0).getClientWithNext(); } next ++; next = next % size; return clientGroupList.get(next % size).getClientWithNext(); } return null; } ``` #### Redis操作 ##### RedisSource ```java public class RedisSource { //通过key获取value public String get(String key) {} //设置key、value public String put(String key, String value) {} //设置key、value,返回是否成功 public boolean putAndSuccess(String key, String value) {} //设置key、value,key过期时间 public String putWithTime(String key, int seconds, String value) {} //设置key过期时间 public boolean setExpireTime(String key, int seconds) {} //取消key过期时间 public boolean delExpireTime(String key) {} //获取key剩余时间 public long getRemainTime(String key) {} //判断key是否存在 public boolean exists(String key) {} //移除key public boolean remove(String key) {} //获取key的类型 public String getType(String key) {} //获取map public RedisMap getMap(String key) {} //获取list public RedisList getList(String key) {} //获取set public RedisSet getSet(String key) {} //获取deque public RedisDeque getDeque(String key) {} //清除redis public void clear() {} } ``` ##### RedisMap 封装了Redis的Hash ```java public class RedisMap implements Map {} ``` ##### RedisList 封装了Redis的List ```java public class RedisList extends RedisCollection implements List {} ``` ##### RedisSet 封装了Redis的Set ```java public class RedisSet implements Set {} ``` ##### RedisDeque 封装了Redis的List ```java public class RedisDeque extends RedisCollection implements Deque {} ``` ##### RedisCollection ```java public abstract class RedisCollection implements Collection {} ```