Giter Club home page Giter Club logo

peonyframwork's Introduction

欢迎来到PeonyFramwork

框架详述

PeonyFramwork是一个优秀的java后端框架,上手简单,使用灵活方便。可以应用于网络游戏,网络应用的服务端开发。目前已经应用于10余款网络游戏的服务端开发,并正在被更多的游戏使用

该框架实现的目标包括:

  1. 最大限度的减少和业务逻辑无关的工作,确保开发者把更多的精力放在业务逻辑上
  2. 组件式开发,更多的通用型功能性组件,以service的方式复用
  3. 容易上手,简单的了解之后即可应用于生产,对技术积累较少的企业,创业团队,学生党,独立开发者更加友好
  4. all in one,将更多服务器周边工具集成进来,包括集群,部署,统计,GM等,将该框架扩展为java后端解决方案

最大限度的减少和业务逻辑无关的工作,包括但不仅限于:

  1. 注解驱动:通过注解来完成服务器中很多的功能或实现特定效果
  2. 服务层事务:提供方法级别的事务机制,确保方法的原子性
  3. 框架层解决缓存问题:用户无需考虑自己为功能做缓存,框架的数据管理机制包含了数据的缓存
  4. 框架层解决并发一致性问题:用户无需考虑并发一致性问题,框架的事务机制和数据管理机制解决了并发一致性问题
  5. 数据库异步更新:数据的增加,删除和修改是通过异步方式实现的
  6. 数据库表自动更新:数据库中的表可以根据业务中的数据对象自动创建,修改,无需手动写sql进行数据库表的创建修改等操作
  7. Service组件规范与定制型配置[倡导通用型组件](还未实现)

下面是如何启动PeonyFramwork,以及基本的使用;如需了解更详细框架使用和原理,请转到PeonyFramwork Wiki

启动框架

1、新建一个maven项目,导入peony

<dependency>
    <groupId>com.github.xuerong</groupId>
    <artifactId>peony-starter</artifactId>
    <version>1.0.1-RELEASE</version>
</dependency>

2、在resource中创建peony配置文件mmserver.properties,添加数据库配置。(不用创建表)

jdbc.type=mysql
# Mysql 版本<= Mysql5
# jdbc.driver=com.mysql.jdbc.Driver
# Mysql 版本>= Mysql6
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/peony?autoReconnect=true&charset=utf8mb4&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=admin123

3、创建Main类,在main方法中调用peony的启动方法。

public class Main {
    public static void main(String[] args){
        Server.start();
    }
}

4、右击,启动。启动成功的标志如下:

-------------------------------------------------------------
|                      Congratulations                      |
|              server(ID:  1) startup success!              |
-------------------------------------------------------------

5、peony使用的日志为logback,默认日志级别为DEBUG。启动日志较多,可在resource中添加logback.xml调整日志级别为INFO。

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="30 second">
    <!-- 控制台打印 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="utf-8">
            <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %m%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

基本使用

以下以框架提供的消息入口WebSocketEntrance和json协议为例,实现一个简单的背包功能

一、创建应用包

  1. 创建应用包。比如”com.peony.peony.peonydemo“。

  2. 在mmserver.properties中添加应用包配置。

    appPackage = com.peony.peony.peonydemo
  3. 在mmserver.properties中添加入口配置。

    entrance.request.port = 9002
    entrance.request.class = com.peony.entrance.websocket_json.WebSocketEntrance
  4. 在com.peony.peony.peonydemo下面创建背包功能的包bag

二. 在bag包下面定义一个背包存储类"DBEntity",如下:

package com.peony.peony.peonydemo.bag;

import com.peony.core.data.persistence.orm.annotation.DBEntity;

import java.io.Serializable;

@DBEntity(tableName = "bag",pks = {"uid","itemId"})
public class BagItem implements Serializable {
    private String uid; // 玩家id
    private int itemId; // 物品id
    private int num; // 物品数量

    // get set 方法
    
    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}
  • 注解@DBEntity声明一个类为背包类,对应数据库中一张表,其参数tableName对应表名,pks对应表的主键,可以为多个。
  • @DBEntity声明的类必须继承Serializable接口,并对参数实现get set方法
  • 表不用手动创建,系统启动时会自动在数据库中创建,大多数的修改也会进行同步,所以不要声明不需要存储数据库的字段

三. 在bag包下面定义一个背包处理服务类"Service",如下:

package com.peony.peony.peonydemo.bag;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.peony.common.exception.ToClientException;
import com.peony.core.control.annotation.Request;
import com.peony.core.control.annotation.Service;
import com.peony.core.data.DataService;
import com.peony.core.data.entity.session.Session;
import com.peony.core.data.tx.Tx;
import com.peony.core.server.SysConstantDefine;

import java.util.List;

@Service
public class BagService {

    private DataService dataService;
    
    /**
     * 协议,获取背包列表
     */
    @Request(opcode = 20011)
    public JSONObject getBagInfos(JSONObject req, Session session) {
        List<BagItem> bagItemList = dataService.selectList(BagItem.class,"uid=?",session.getUid());
        JSONArray array= JSONArray.parseArray(JSON.toJSONString(bagItemList));
        JSONObject ret = new JSONObject();
        ret.put("bagItems",array);
        return ret;
    }
    
    /**
     * 协议,添加背包信息
     */
    @Request(opcode = 20012)
    public JSONObject addBagInfo(JSONObject req, Session session) {
        int currNum = addItem(session.getUid(),req.getInteger("itemId"),req.getInteger("num"));
        JSONObject ret = new JSONObject();
        ret.put("currNum",currNum);
        return ret;
    }

    /**
     * 添加物品到背包
     */
    @Tx
    public int addItem(String uid,int itemId,int num){
        BagItem bagItem =  dataService.selectObject(BagItem.class,"uid=? and itemId=?",uid,itemId);
        if(bagItem == null){
            bagItem = new BagItem();
            bagItem.setUid(uid);
            bagItem.setItemId(itemId);
            bagItem.setNum(num);
            dataService.insert(bagItem);
        }else{
            bagItem.setNum(bagItem.getNum()+num);
            dataService.update(bagItem);
        }
        return bagItem.getNum();
    }

    /**
     * 从背包中删除物品
     */
    @Tx
    public int decItem(String uid,int itemId,int num){
        BagItem bagItem =  dataService.selectObject(BagItem.class,"uid=? and itemId=?",uid,itemId);
        if(bagItem == null){
            throw new ToClientException(SysConstantDefine.InvalidParam,"item is not enough1,itemId={}",itemId);
        }
        if(bagItem.getNum()<num){
            throw new ToClientException(SysConstantDefine.InvalidParam,"item is not enough2,itemId={}",itemId);
        }
        bagItem.setNum(bagItem.getNum()-num);
        dataService.update(bagItem);
        return bagItem.getNum();
    }
}
  • 注解@Service声明一个类为一个服务类
  • 声明了数据服务类DataService,该类中提供了对数据操作的所有接口。 引用其他服务类时,声明即可使用,系统启动时会进行依赖注入
  • @Request声明一个方法为处理客户端协议的方法,其中opcode为协议号(整型),因为使用的是json协议, 参数类型必须为JSONObject和Session,返回值类型为JSONObject。其中参数JSONObject为客户端发送过来的消息 Session中有玩家基本信息,包括玩家账号session.getUid()
  • 注解@Tx可以确保整个方法的执行在服务层事务中,确保业务失败的数据回滚和并发情况下的数据一致问题

四. 前端调用

  1. 本地启动服务器

  2. 打开websocket在线工具:http://www.blue-zero.com/WebSocket/

  3. 在地址输入框中输入:ws://localhost:9002/websocket,点击连接。如果出现"连接已建立,正在等待数据...",说明websocket连接成

  4. 在发送消息框中输入登录消息如下:

{
    "id": "102", 
    "data": {
        "accountId": "test1"
    }
}

其中,id为登录消息协议号,accountId为登录的账号
点击发送,可得到登录成功的返回:

{
    "data": {
        "accountId": "test2", 
        "newUser": 1, 
        "serverTime": 1540206073929
    }, 
    "id": 102
}

其中newUser标识为新用户,serverTime为服务器时间,id和accountId同上

  1. 登陆完成后,发送添加道具的接口
{
    "id": "20012", 
    "data": {
        "itemId":1,
        "num":2
    }
}

可以收到回复

{
    "data": {
        "currNum": 2
    }, 
    "id": 20012
}
  1. 然后就可以进行背包信息的获取了,发送消息
{
    "id": "20011", 
    "data": {
        
    }
}

可收到消息:

{
    "data": {
        "bagItems": [
            {
                "itemId": 1, 
                "uid": "test1", 
                "num": 2
            }
        ]
    }, 
    "id": 20011
}

框架详述

框架详述

peonyframwork's People

Contributors

xuerong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

peonyframwork's Issues

登陆显示没找到主键方法

按照README操作,本地环境可以连接,但是发送消息框中输入登录消息是提示没有找到主键方法是为什么?
21:51:00.326 [defaultEventExecutorGroup-4-1] ERROR com.peony.entrance.websocket_json.WebSocketEntrance - handler msg exception:
com.peony.common.exception.MMException: 没找到主键方法 com.peony.core.data.entity.account.Account
at com.peony.core.data.cache.KeyParser.parseKeyForObject(KeyParser.java:58)

启动框架报错

1、找不到build.gradle,只有build.gradle.old文件,将build.gradle.old修改为build.gradle,打开项目后报错 The Key=com.peony.demo.packet.Main-Class violates the Manifest spec!
2、找不到到启动类com.peony.engine.framework.server.Server

RequestNettyPBDecoder 类解析半包问题

RequestNettyPBDecoder 类的 decode() 在半包读取时出现 opcode 丢失问题。
问题原因是 ByteBuf#readableBytes() 满足 headSize 长度但不满足消息体时,也就是

if (size > readAble - headSize) {
    return;
}

代码段,在下次读取剩余半包时消息体正常读取,但 opcode 和 id 是局部变量而不是方法变量导致为 0,最后抛出 MMException

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.