前言

RuoYi-Cloud是基于Spring Cloud的微服务权限管理系统,采用前后端分离架构。本文介绍如何使用Nacos进行微服务管理,以及单机调试与微服务模式部署的方法。

技术栈

技术 说明
Spring Cloud 微服务框架
Spring Cloud Alibaba 阿里巴巴微服务组件
Nacos 服务注册与配置中心
Gateway 网关服务
Sentinel 流量控制
Seata 分布式事务
MySQL 数据库
Redis 缓存

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ruoyi-cloud/
├── ruoyi-api/ # API模块
│ ├── ruoyi-api-system/ # 系统API
│ └── ruoyi-api-file/ # 文件API
├── ruoyi-auth/ # 认证中心
├── ruoyi-common/ # 通用模块
│ ├── ruoyi-common-core/ # 核心模块
│ ├── ruoyi-common-redis/ # Redis模块
│ ├── ruoyi-common-security/ # 安全模块
│ └── ruoyi-common-swagger/ # Swagger模块
├── ruoyi-gateway/ # 网关服务
├── ruoyi-modules/ # 业务模块
│ ├── ruoyi-system/ # 系统模块
│ ├── ruoyi-gen/ # 代码生成
│ ├── ruoyi-job/ # 定时任务
│ └── ruoyi-file/ # 文件服务
├── ruoyi-visual/ # 可视化模块
│ └── ruoyi-monitor/ # 监控中心
└── ruoyi-ui/ # 前端项目

自定义模块结构

按照你提供的结构,创建自定义业务模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
${artifactId}/
├─ pom.xml # 根pom(带profiles)
├─ ${artifactId}-api/ # API聚合模块
│ ├─ ${artifactId}-local-api/ # 单机启动模块
│ │ ├─ pom.xml
│ │ └─ src/main/java/...
│ ├─ ${artifactId}-cloud-api/ # 微服务启动模块
│ │ ├─ pom.xml
│ │ └─ src/main/java/...
│ └─ pom.xml
├─ ${artifactId}-biz/ # 业务逻辑模块
│ ├─ pom.xml
│ └─ src/main/java/...
└─ ${artifactId}-start/ # 启动模块
├─ pom.xml
└─ src/main/java/...

Nacos配置

1. 安装Nacos

1
2
3
4
5
6
7
8
9
10
# 下载Nacos
wget https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.tar.gz
tar -xvf nacos-server-2.2.0.tar.gz
cd nacos/bin

# 单机模式启动
./startup.sh -m standalone

# Windows
startup.cmd -m standalone

访问 http://localhost:8848/nacos,默认账号密码:nacos/nacos

2. 服务注册配置

1
2
3
4
5
6
7
8
9
10
11
12
13
# application.yml
spring:
application:
name: ruoyi-system
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
config:
server-addr: 127.0.0.1:8848
namespace: dev
file-extension: yml

3. 配置中心使用

在Nacos控制台创建配置:

  • Data ID: ruoyi-system-dev.yml
  • Group: DEFAULT_GROUP
1
2
3
4
5
6
7
8
9
10
# Nacos配置内容
server:
port: 9201

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true
username: root
password: password

单机调试模式

Profile配置

根pom.xml配置profiles:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<profiles>
<!-- 单机模式 -->
<profile>
<id>local</id>
<properties>
<profile.active>local</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 微服务模式 -->
<profile>
<id>cloud</id>
<properties>
<profile.active>cloud</profile.active>
</properties>
</profile>
</profiles>

单机启动配置

application-local.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
profiles:
active: local
cloud:
nacos:
discovery:
enabled: false # 禁用服务注册
config:
enabled: false # 禁用配置中心

# 直接配置数据源
spring:
datasource:
url: jdbc:mysql://localhost:3306/ry-cloud
username: root
password: password

单机启动命令

1
2
3
4
# 使用local profile启动
mvn spring-boot:run -Plocal

# 或者IDEA中设置Active Profiles为local

微服务模式部署

1. 启动顺序

1
2
3
4
5
6
1. Nacos (服务注册与配置中心)
2. MySQL + Redis
3. ruoyi-gateway (网关)
4. ruoyi-auth (认证中心)
5. ruoyi-system (系统服务)
6. 其他业务服务

2. Docker Compose部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
version: '3'
services:
nacos:
image: nacos/nacos-server:v2.2.0
environment:
- MODE=standalone
ports:
- "8848:8848"

mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
ports:
- "3306:3306"

redis:
image: redis:6.0
ports:
- "6379:6379"

ruoyi-gateway:
build: ./ruoyi-gateway
ports:
- "8080:8080"
depends_on:
- nacos

ruoyi-auth:
build: ./ruoyi-auth
depends_on:
- nacos
- redis

ruoyi-system:
build: ./ruoyi-modules/ruoyi-system
depends_on:
- nacos
- mysql

3. 微服务启动命令

1
2
3
4
5
6
7
8
# 使用cloud profile启动
mvn spring-boot:run -Pcloud

# 打包
mvn clean package -Pcloud

# 运行jar
java -jar ruoyi-gateway.jar --spring.profiles.active=cloud

服务调用

Feign客户端定义

1
2
3
4
5
6
7
8
9
// ruoyi-api-system模块
@FeignClient(contextId = "remoteUserService",
value = "ruoyi-system",
fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService {

@GetMapping("/user/info/{username}")
R<LoginUser> getUserInfo(@PathVariable("username") String username);
}

服务调用

1
2
3
4
5
6
7
8
9
10
11
@Service
public class SysLoginService {

@Autowired
private RemoteUserService remoteUserService;

public LoginUser login(String username) {
R<LoginUser> result = remoteUserService.getUserInfo(username);
return result.getData();
}
}

常见问题

1. Nacos连接失败

检查Nacos是否启动,namespace是否正确配置

2. 服务注册不上

检查spring.application.name是否配置,网络是否通畅

3. 配置不生效

确认Data ID格式:${spring.application.name}-${profile}.${file-extension}

总结

RuoYi-Cloud通过Nacos实现了服务注册与配置管理,支持单机和微服务两种部署模式。开发时使用单机模式方便调试,生产环境使用微服务模式实现高可用。

参考资料

评论和共享

前言

ET框架是一个基于C#的双端(客户端+服务端)游戏开发框架,由熊猫大佬开发维护。它的最大特点是客户端和服务端共用一套代码,大大提高了开发效率。本文将介绍ET框架的核心概念和简单入门。

ET框架特点

1. 双端共用代码

  • 客户端使用Unity引擎
  • 服务端使用.NET Core
  • 网络协议、实体组件等代码可以共用

2. ECS架构

ET采用Entity-Component-System架构:

  • Entity(实体):游戏中的对象,如玩家、怪物
  • Component(组件):实体的数据和行为
  • System(系统):处理组件的逻辑

3. Actor模型

服务端采用Actor模型进行消息处理,每个Actor独立处理自己的消息队列,避免多线程竞争。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ET/
├── Unity/ # Unity客户端项目
│ ├── Assets/
│ │ ├── Scripts/
│ │ │ ├── Core/ # 核心框架代码
│ │ │ ├── Model/ # 数据模型
│ │ │ ├── Hotfix/ # 热更新代码
│ │ │ └── ThirdParty/ # 第三方库
│ │ └── Bundles/ # 资源包
├── Server/ # 服务端项目
│ ├── Model/ # 服务端数据模型
│ ├── Hotfix/ # 服务端热更新
│ └── App/ # 启动入口
└── Share/ # 共享代码
├── Proto/ # 协议定义
└── Config/ # 配置文件

核心概念

Entity 实体

1
2
3
4
5
6
7
8
9
10
11
// 定义一个玩家实体
public class Player : Entity
{
public string Name { get; set; }
public int Level { get; set; }
}

// 创建实体
Player player = ComponentFactory.Create<Player>();
player.Name = "TestPlayer";
player.Level = 1;

Component 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义移动组件
public class MoveComponent : Component
{
public Vector3 Position { get; set; }
public float Speed { get; set; }

public void MoveTo(Vector3 target)
{
// 移动逻辑
}
}

// 给实体添加组件
player.AddComponent<MoveComponent>();

消息处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义消息
[Message(OuterOpcode.C2G_Login)]
public partial class C2G_LoginRequest : IRequest
{
public string Account { get; set; }
public string Password { get; set; }
}

// 消息处理器
[MessageHandler]
public class C2G_LoginHandler : AMRpcHandler<C2G_LoginRequest, G2C_LoginResponse>
{
protected override async ETTask Run(Session session, C2G_LoginRequest request,
G2C_LoginResponse response, Action reply)
{
// 处理登录逻辑
response.PlayerId = 10001;
reply();
}
}

快速入门

1. 环境准备

1
2
3
4
5
# 安装 .NET Core SDK 3.1+
# 安装 Unity 2019.4 LTS

# 克隆项目
git clone https://github.com/egametang/ET.git

2. 启动服务端

1
2
cd ET/Server/App
dotnet run

3. 启动客户端

  1. 用Unity打开 ET/Unity 项目
  2. 打开场景 Assets/Scenes/Init.unity
  3. 点击Play运行

4. 创建第一个功能

以创建一个简单的聊天功能为例:

定义协议 (Share/Proto/OuterMessage.proto)

1
2
3
4
5
6
7
8
9
10
11
message C2G_Chat // IRequest
{
string Content = 1;
}

message G2C_Chat // IResponse
{
int32 RpcId = 90;
int32 Error = 91;
string Message = 92;
}

服务端处理器

1
2
3
4
5
6
7
8
9
10
11
[MessageHandler]
public class C2G_ChatHandler : AMRpcHandler<C2G_Chat, G2C_Chat>
{
protected override async ETTask Run(Session session, C2G_Chat request,
G2C_Chat response, Action reply)
{
Log.Info($"收到聊天消息: {request.Content}");
response.Message = $"服务器收到: {request.Content}";
reply();
}
}

客户端调用

1
2
3
4
5
6
public static async ETTask SendChat(string content)
{
G2C_Chat response = await SessionComponent.Instance.Session
.Call(new C2G_Chat { Content = content }) as G2C_Chat;
Log.Info(response.Message);
}

服务端架构

ET服务端采用分布式架构,主要包含以下进程:

进程 说明
Gate 网关服务器,处理客户端连接
Map 地图服务器,处理游戏逻辑
Location 位置服务器,管理Actor位置
DB 数据库代理服务器
Realm 登录服务器

热更新机制

ET支持代码热更新:

  • Model层:不可热更新的基础代码
  • Hotfix层:可热更新的业务逻辑
1
2
// Hotfix层代码可以在运行时重新加载
// 修改Hotfix代码后,服务端可以不停服更新

总结

ET框架的优势:

  1. 开发效率高:双端共用代码
  2. 架构清晰:ECS + Actor模型
  3. 支持热更新:业务逻辑可热更
  4. 社区活跃:持续更新维护

适用场景:

  • 中小型网络游戏
  • 需要快速迭代的项目
  • 熟悉C#的团队

参考资料

评论和共享

容器简介

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。

CentOS 7 安装docker

安装必要的一些系统工具

1
$ yum update
1
$ yum install -y yum-utils device-mapper-persistent-data lvm2

添加软件源信息

1
$ yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

更新并安装 Docker

1
2
3
4
5
6
7
$ yum makecache fast
$ yum -y install docker-ce


$ echo 开启容器服务
$ systemctl enable docker
$ systemctl start docker

注意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,你可以通过以下方式开启。同理可以开启各种测试版本等。
$ vim /etc/yum.repos.d/docker-ce.repo
$ 将 [docker-ce-test] 下方的 enabled=0 修改为 enabled=1
$
$ 安装指定版本的Docker-CE:
$ Step 1: 查找Docker-CE的版本:
$ yum list docker-ce.x86_64 --showduplicates | sort -r
$ Loading mirror speeds from cached hostfile
$ Loaded plugins: branch, fastestmirror, langpacks
$ docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
$ docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
$ docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
$ Available Packages
$ Step2 : 安装指定版本的Docker-CE: (VERSION 例如上面的 17.03.0.ce.1-1.el7.centos)
$ yum -y install docker-ce-[VERSION]

注册一个阿里的账号,
进入加速器页面https://cr.console.aliyun.com/#/accelerator

1
$ vim /etc/docker/daemon.json

内容如下

1
2
3
{
"registry-mirrors": ["https://你的阿里云加速地址.mirror.aliyuncs.com"]
}

重启docker

1
2
$ systemctl daemon-reload
$ systemctl restart docker

常用docker命令

查看运行容器

1
$ docker ps

查看所有容器

1
$ docker ps -a

进入容器
其中字符串为容器ID:

1
$ docker exec -it d27bd3008ad9 /bin/bash

1.停用全部运行中的容器:

1
$ docker stop $(docker ps -q)

2.删除全部容器:

1
$ docker rm $(docker ps -aq)

3.一条命令实现停用并删除容器:

1
$ docker stop $(docker ps -q) && docker rm $(docker ps -aq)

安装mariadb

centOS上比yum安装mysql要方便.

###安装mysql-client,用于访问mysql容器数据库

1
$ yum -y install mariadb

单独MySQL模式

1
2
3
4
5
6
7
docker run -d \
-e TIMEZONE=Asia/Shanghai \
-v /data/mariadb-master:/data/mariadb \
-e MYSQL_ROOT_PASSWORD=admin \
-e REPLICATION_PASSWORD=admin \
-p 3306:3306 \
mariadb

MariaDB MASTER

进入容器需要修改镜像
将下面内容添加进/etc/mysql/conf.d/*.cnf
从机server-id=2

1
2
3
4
5
[mysqld]
server-id=1
log-bin=mysql-bin
binlog_format=MIXED
default-time_zone = '+8:00'

提交

1
2
docker commit -m "set log bin on" -a "lional" d27bd3008ad9 mariadb-master:1.0
docker commit -m "set log bin on" -a "lional" d27bd3008ad9 mariadb-slave:1.0

运行master, 这里LOG_BIN参数没有用,所以在上面修改了镜像

1
2
3
4
5
6
7
8
9
10
11
docker run -d \
--restart=always \
--name mysql-master \
-e TIMEZONE=Asia/Shanghai \
-v /data/mariadb-master:/data/mariadb \
-e MYSQL_ROOT_PASSWORD=admin \
-e LOG_BIN=mysql-bin \
-e BINLOG_FORMAT=MIXED \
-e REPLICATION_PASSWORD=admin \
-p 3306:3306 \
mariadb-master:1.0

查询容器的ip地址

1
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' $(docker ps -a -q)
1
172.17.0.2

链接容器mysql

1
mysql -uroot -padmin -h 172.17.0.2

查看master是否启用了log_bin

1
2
3
4
5
6
7
8
9
10
11
12
MariaDB [(none)]> show variables like '%log_bin%';
+---------------------------------+--------------------------------+
| Variable_name | Value |
+---------------------------------+--------------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/mysql-bin |
| log_bin_compress | OFF |
| log_bin_compress_min_len | 256 |
| log_bin_index | /var/lib/mysql/mysql-bin.index |
| log_bin_trust_function_creators | OFF |
| sql_log_bin | ON |
+---------------------------------+--------------------------------+

查看主机状态,注意启动从机的时候bin和pos的参数

1
2
3
4
5
6
MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 | 342 | | |
+------------------+----------+--------------+------------------+

添加slave账户

1
2
GRANT REPLICATION SLAVE ON *.* to 'slave'@'%' identified by 'reader';
FLUSH PRIVILEGES;

MariaDB SLAVE

没有sql导入的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -d \
--restart=always \
--name mysql-slave2 \
-e TIMEZONE=Asia/Shanghai \
-v /data/mariadb-slave:/data/mariadb \
-e MYSQL_ROOT_PASSWORD=admin \
-e LOG_BIN=mysql-bin \
-e BINLOG_FORMAT=MIXED \
-e REPLICATION_PASSWORD=admin \
-e MASTER_LOG_FILE=mysql-bin.000003 \
-e MASTER_LOG_POS=342 \
-e MASTER_PORT=3306 \
-e MASTER_HOST=172.17.0.2 \
-p 3310:3306 \
mariadb-slave2:1.0

先用sql文件导入再同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -d \
--restart=always \
--name mysql-master \
-e TIMEZONE=Asia/Shanghai \
-v /data/mariadb-slave:/data/mariadb \
-e MYSQL_ROOT_PASSWORD=admin \
-e BINLOG_FORMAT=MIXED \
-e REPLICATION_PASSWORD=admin \
-e DATABASE_FILE=database.sql \
-e MASTER_LOG_FILE=mysql-bin.000003 \
-e MASTER_LOG_POS=342 \
-e MASTER_PORT=3306 \
-e MASTER_HOST=172.17.0.2 \
-p 3308:3306 \
mariadb-slave:1.0

需要把database.sql文件存提前存放在/data/mariadb-slave目录

进入slaver的mariadb

1
mysql -uroot -padmin -h 172.17.0.3

设置主库链接

1
change master to master_host='172.17.0.2',master_user='slave',master_password='reader',master_log_file='mysql-bin.000003',master_log_pos=342,master_port=3306;

启动从库同步

1
start slave;

查看状态

1
show slave status\G;

当看到

1
2
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

则启动成功

##docker redis 集群(cluster)搭建

顺带完成redis集群,安装依赖

1
$ docker pull redis
1
$ docker pull ruby

创建redis容器

1、创建redis配置文件(redis-cluster.tmpl)

我在路径/root下创建一个文件夹redis-cluster,在路径/root/redis-cluster下创建一个文件redis-cluster.tmpl,并把以下内容复制过去。

10.0.2.15 //自己的服务器ip

1
2
3
4
5
6
7
8
port ${PORT}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 10.0.2.15
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
appendonly yes

创建自定义network

1
$ docker network create redis-net

在/root/redis-cluster下生成conf和data目标,并生成配置信息

1
2
3
4
5
for port in `seq 6379 6384`; do \
mkdir -p ./${port}/conf \
&& PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf \
&& mkdir -p ./${port}/data; \
done

创建6个redis容器

1
2
3
4
5
6
7
for port in `seq 6379 6384`; do \
docker run -d -ti -p ${port}:${port} -p 1${port}:1${port} \
-v /root/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /root/redis-cluster/${port}/data:/data \
--restart always --name redis-${port} --net redis-net \
--sysctl net.core.somaxconn=1024 redis redis-server /usr/local/etc/redis/redis.conf; \
done

通过启动ruby来实现集群

1
2
3
4
5
6
7
echo yes | docker run -i --rm --net redis-net ruby sh -c ' \
gem install redis \
&& wget http://download.redis.io/redis-stable/src/redis-trib.rb \
&& ruby redis-trib.rb create --replicas 1 \
'"$(for port in `seq 6379 6384`; do \
echo -n "$(docker inspect --format '{{ (index .NetworkSettings.Networks "redis-net").IPAddress }}' "redis-${port}")":${port} ' ' ; \
done)"

评论和共享

  • 第 1 页 共 1 页

Lional

author.bio


author.job