做了很久其他事情,代码也很少写了,最近还是回归。因为之前公司有个项目本来是考虑分布式的方案来写,但是种种原因(其他项目赶,预估的访问量也很小等),所以就没有搭建。那就自己先本地搭一套,hyperf和nacos都比当年上了一个版本,所以也就用最新的了。
搭建过程中也遇到了很多问题,最开始用deepseek,想要一套完整的docker搭建方案,仿佛给出来的很美妙,享受了一把AI带来的便利,最后折腾了两天,也用了chatgpt,运行不断有报错,一个接一个,根本跑不起来,我也不想耽误时间,就自己写,也只是花了半天的时间就搭建的差不多了。当然,这些ai还是帮助我在短期内解决了不少问题。
搭建过程我没法再复现了,只能把完整的结果列出来,以后还要再这套系统里增加redis和rabbitmq,再模拟一套前端,可能是搭建一套分布式的购物下单流程,这个是最常见的应用场景。用来练练手,希望过年前能全部写完,还要考虑分布式事务的问题,后面要补充的东西还不少,这次只能把搭建基础的东西全都写好。
目录结构
|--infrastructure/
| |--mysql/
| | |--mysql_data/
| |--nacos/
| | |--logs/
|--init-scripts/ #一些同步需要使用的脚本
| |--mysql-init.sql # nacos初始化使用sql语句
|--service/ #代码部分
| |--docker/
| | |--Dockerfile #下面的service使用的是同样的hyperf3镜像
| |--order-service/
| |--payment-service/
|--docker-compose.yml
|--run-first.sh
docker-compose.yml
现在的版本,开头不用加version了,不然好像还会报错。
services:
nacos:
image: nacos/nacos-server:v3.1.1
container_name: nacos-server
ports:
- "8080:8080"
- "8848:8848"
- "9848:9848"
environment:
- PREFER_HOST_MODE=hostname
- MODE=standalone
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_DB_NAME=nacos_config
- MYSQL_SERVICE_PORT=3306
- MYSQL_SERVICE_USER=nacos
- MYSQL_SERVICE_PASSWORD=nacos
- MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
- NACOS_AUTH_IDENTITY_KEY=2222
- NACOS_AUTH_IDENTITY_VALUE=2xxx
- NACOS_AUTH_TOKEN=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
- NACOS_AUTH_IDENTITY_KEY=SecretKey012345678901234567890123456789012345678901234567890123456789
volumes:
- ./mysql-init:/docker-entrypoint-initdb.d/
- ./infrastructure/nacos/logs:/home/nacos/logs
# - ./infrastructure/nacos/conf:/home/nacos/conf
networks:
- order-network
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
container_name: mysql-server
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: nacos_config
MYSQL_USER: nacos
MYSQL_PASSWORD: nacos
volumes:
- ./infrastructure/mysql/mysql_data:/var/lib/mysql
- ./init-scripts/mysql-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- order-network
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
order-service:
container_name: order-service
image: hyperf-skeleton:latest
build:
# 只有一个service使用build中的dockerfile
# 之前在payment-service中也加了这条,运行后会出现一个none的镜像
dockerfile: ./service/docker/Dockerfile
args:
UID: 1000
GID: 1000
# volumes这行先注释掉,因为会在容器启动时生成代码,
# 如果不注释掉,会被宿主机的空目录把代码覆盖掉,下面payment-service的也是一样。
# 等容器启动后,把代码从容器中复制到宿住机的这个目录,再从新启动容器即可
#volumes:
# - ./service/order-service:/opt/www
ports:
- 9501:9501
networks:
- order-network
environment:
- APP_ENV=dev
- SCAN_CACHEABLE=false
payment-service:
container_name: payment-service
image: hyperf-skeleton:latest
#volumes:
# - ./service/payment-service:/opt/www
ports:
- 9502:9501
networks:
- order-network
environment:
- APP_ENV=dev
- SCAN_CACHEABLE=false
networks:
order-network:
driver: bridge
/service/docker/Dockerfile
FROM hyperf/hyperf:8.4-alpine-v3.21-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Order"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
ARG UID=1000
ARG GID=1000
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
APP_ENV=dev \
SCAN_CACHEABLE=(false)
# 暂时没发现作用..这个是ai遗留产物,回头研究下
# Make local user to avoid file permissions on runtime
RUN addgroup -g ${GID} application && \
adduser -S -D -u ${UID} -G application -s /bin/ash -h /home/application application
# update
RUN set -ex \
# show php version and extensions
&& php -v \
&& php -m \
&& php --ri swoole \
# ---------- some config ----------
&& cd /etc/php* \
# - config PHP
&& { \
echo "upload_max_filesize=128M"; \
echo "post_max_size=128M"; \
echo "memory_limit=1G"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99_overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- clear works ----------
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
RUN chmod +x /usr/local/bin/composer
USER application
WORKDIR /opt/www
# 这里是防止重复创建项目,因为第一次运行过后,本地会保留代码,就不用重复执行了
RUN if [ ! -f composer.json ]; then \
composer --version; \
composer create-project hyperf/hyperf-skeleton /tmp/hyperf --no-interaction --ignore-platform-reqs; \
cp -r /tmp/hyperf/. /opt/www/; \
rm -rf /tmp/hyperf; \
rm -f composer.lock; \
fi
# 如果宿主机有手动composer.json,那么需要重新执行install
RUN rm -f composer.lock; \
composer install --no-dev --optimize-autoloader --no-interaction;
EXPOSE 9501
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
mysql-init.sql 初始化nacos的sql语句
/******************************************/
/* 表名称 = config_info */
/******************************************/
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) DEFAULT NULL COMMENT 'group_id',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description',
`c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage',
`effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述',
`type` varchar(64) DEFAULT NULL COMMENT '配置的类型',
`c_schema` text COMMENT '配置的模式',
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 表名称 = config_info since 2.5.0 */
/******************************************/
CREATE TABLE `config_info_gray` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`src_user` text COMMENT 'src_user',
`src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip',
`gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create',
`gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`gray_name` varchar(128) NOT NULL COMMENT 'gray_name',
`gray_rule` text NOT NULL COMMENT 'gray_rule',
`encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`),
KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`),
KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='config_info_gray';
/******************************************/
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(20) unsigned NOT NULL COMMENT 'id',
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`op_type` char(10) DEFAULT NULL COMMENT 'operation type',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
`publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal',
`gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name',
`ext_info` longtext DEFAULT NULL COMMENT 'ext info',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username',
`password` varchar(500) NOT NULL COMMENT 'password',
`enabled` boolean NOT NULL COMMENT 'enabled'
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL COMMENT 'username',
`role` varchar(50) NOT NULL COMMENT 'role',
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL COMMENT 'role',
`resource` varchar(128) NOT NULL COMMENT 'resource',
`action` varchar(8) NOT NULL COMMENT 'action',
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
run-first.sh
这个脚本就是第一次执行,且只需要运行一次即可,前期建议手动执行。
/bin/sh
# 首次运行使用
docker compose down #清理容器
docker compose build #构建镜像
dokcer compose up -d #启动容器
# 从容器中复制代码到宿主机,如果没法复制,可能是容器还未启动,就等容器完全启动后,手动执行。后面增加检查容器启动情况
docker cp order-service:/opt/www/. ./service/order-service
docker cp payment-service:/opt/www/. ./service/payment-service
修改docker-compose.yml文件,把order-service和payment-service中的volumes注释去掉,再执行下面的命令
docker compose down
docker compose build
dokcer compose up -d
这样完整的项目就都有了,可以开始撸代码了。后续要写hyperf和nacos交互的代码了,大多数都是以配置为主。