使用SpringBoot进行游戏服务器开发

背景:

之前一直只考虑用JavaSe进行游戏服务器开发,目前项目使用了Spring,发现还是非常好的,好处如下:

        好处1:依赖注入非常方便,我们只使用Spring最基本的功能即可,这样子就算是有一些模块不使用Spring管理也是非常方便的,因为我现在已经能轻松控制住Spring容器的声明周期。

        好处2: 模块之间就像搭建积木即可,又相互配合。 我想支持web也是非常轻松。

        好处3: 这样子再去整合Mybatis、或者其它的一些MQ、ES之类的中间件,就太简单了。

1.net模块

pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.17
         
    

    com.example
    net
    0.0.1-SNAPSHOT
    net
    pom
    net
    
        UTF-8
        11
        11
        11
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.projectlombok
            lombok
            true
        

        
            org.projectlombok
            lombok
            true
        
        
            io.lettuce
            lettuce-core
        
    


TcpServer.java

package com.example.net;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TcpServer {
    private String host;
    private int port;

    public TcpServer(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start1() {
        log.info("TcpServer start1 {}:{}", host, port);
    }

    public void shutdown1() {
        log.info("TcpServer shutdown1");
    }
}

GameRedisClient.java

package com.example.net;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.sync.RedisCommands;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

@SuppressWarnings("all")
public class GameRedisClient {
    private final RedisCommands redisCommands;

    public GameRedisClient(String host, int port) {
        RedisURI redisURI = RedisURI.builder()
                .withHost(host)
                .withPort(port)
                .withTimeout(Duration.of(10, ChronoUnit.SECONDS))
                .build();

        RedisClient redisClient = RedisClient.create(redisURI);

        RedisCommands redisCommands = redisClient.connect().sync();

        this.redisCommands = redisCommands;
    }

    public  V get(K k) {
        return (V) redisCommands.get(k);
    }

    public  void set(K k, V v) {
        redisCommands.set(k, v);
    }
}

2.应用层

pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.17
         
    
    com.example
    gameserver
    0.0.1-SNAPSHOT
    gameserver
    Demo project for Spring Boot
    
        UTF-8
        11
        11
        11
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.example
            net
            0.0.1-SNAPSHOT
            compile
        
        
            com.alibaba
            fastjson
            1.2.48
            compile
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        paketobuildpacks/builder-jammy-base:latest
                    
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    


GameServer.java

package com.example;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.IOException;

@Slf4j
@SpringBootApplication
public class GameServer {
    public static void main(String[] args) {
        // 启动Spring容器
        SpringApplication.run(GameServer.class, args);

        // jmx阻塞关服
        try {
            System.in.read();
        } catch (IOException e) {
            log.error("exception", e);
        }
    }
}

ModuleConfig.java

package com.example.core.config;

import com.example.net.GameRedisClient;
import com.example.net.TcpServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 引入子模块对象
 */
@Configuration
public class ModuleConfig {
    /**
     * 游戏Redis
     */
    @Bean
    public GameRedisClient getGameRedis() {
        return new GameRedisClient("localhost", 6379);
    }

    /**
     * 长链接服务器
     */
    @Bean
    public TcpServer getTcpServer() {
        return new TcpServer("localhost", 6080);
    }
}

EModuleOrder.java

package com.example.core.enums;

/**
 * 越靠上越先加载,各个模块的启动
 */
public enum EModuleOrder {
    TCP_SERVER,
    GAME_REDIS,
}

GameRedisModule.java

package com.example.core.module;

import com.alibaba.fastjson.JSON;
import com.example.core.enums.EModuleOrder;
import com.example.net.GameRedisClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class GameRedisModule implements ApplicationListener, Ordered {

    @Autowired
    GameRedisClient gameRedisClient;

    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            log.info("GameRedisModule start");
            // 简单类型
            gameRedisClient.set("123", "abc");

            String v = gameRedisClient.get("123");

            log.info("{}", v);

            // 复杂类型
            Data data = new Data();
            data.map.put("k", 6666);

            gameRedisClient.set("obj", JSON.toJSONString(data));

            Data obj = JSON.parseObject(gameRedisClient.get("obj"), Data.class);

            log.info("{}", obj);
        } else if (event instanceof ContextClosedEvent) {
            log.info("GameRedisModule shutdown");
        }
    }

    @Override
    public int getOrder() {
        return EModuleOrder.GAME_REDIS.ordinal();
    }

    private static class Data {
        public int num = 1;
        public Map map = new HashMap<>();

        @Override
        public String toString() {
            return "Data{" +
                    "num=" + num +
                    ", map=" + map +
                    '}';
        }
    }

}

TcpServerModule.java

package com.example.core.module;

import com.example.core.enums.EModuleOrder;
import com.example.net.TcpServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class TcpServerModule implements ApplicationListener, Ordered {
    @Autowired
    TcpServer tcpServer;

    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            tcpServer.start1();
        } else if (event instanceof ContextClosedEvent) {
            tcpServer.shutdown1();
        }
    }

    @Override
    public int getOrder() {
        return EModuleOrder.TCP_SERVER.ordinal();
    }
}

感悟:

这样子,我们使用了SpringBoot很少的功能,就去组织我们的代码,各个模块像组件一样搭建起来。

应用层则启动类非常简单,我们就可以不断提供子模块,以强大我们的应用层。

引入MQ什么的也是非常简单了。

你可能感兴趣的:(#,spring,boot,游戏)