一,认识微服务
我们先看看开发大型项目采用单体架构存在哪些问题,而微服务架构又是如何解决这些问题的。
1.1 单体架构
单体架构(monolithic structure):整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。
当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。
但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:
1.1.1 单体架构呈现的问题:
团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。
1.2 微服务
微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人
服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响
就比如我们的购物商城,我们可以把商品,用户,购物车,交易等模块拆分,交给不同的团队去开发,并独立部署:
如果采用这样的开发方式,单体架构出现的问题解决了吗?
团队协作成本高?
由于服务拆分,每个服务代码量大大减少,参与开发的后台人员在1~3名,协作成本大大降低
系统发布效率低?
每个服务都是独立部署,当有某个服务有代码变更时,只需要打包部署该服务即可
系统可用性差?
每个服务独立部署,并且做好服务隔离,使用自己的服务器资源,不会影响到其它服务。
微服务架构解决了单体架构存在的问题,特别适合大型互联网项目的开发,因此被各大互联网公司普遍采用。大家以前可能听说过分布式架构,分布式就是服务拆分的过程,其实微服务架构正是分布式架构的一种最佳实践的方案。
1.2.1 微服务拆分出现的问题
如果出现跨服务的业务该如何处理?
页面请求到底该访问哪个服务?
如何实现各个服务之间的服务隔离?
1.3 springcloud
微服务拆分以后碰到的各种问题都有对应的解决方案和微服务组件,而SpringCloud框架可以说是目前Java领域最全面的微服务组件的集合了。
而且SpringCloud依托于SpringBoot的自动装配能力,大大降低了其项目搭建、组件使用的成本。对于没有自研微服务组件能力的中小型企业,使用SpringCloud全家桶来实现微服务开发可以说是最合适的选择了!
1.3.1 springcloud官网
Spring Cloud
目前SpringCloud最新版本为2023.0.x
版本,对应的SpringBoot版本为3.2.x
版本,但2022及后续版本全部依赖于JDK17,目前在企业中使用相对较少。
SpringCloud版本 | SpringBoot版本 |
---|---|
2023.0.x aka Leyton | 3.2.x |
2022.0.x aka Kilburn | 3.0.x |
2021.0.x aka Jubilee | 2.6.x, 2.7.x (Starting with 2021.0.3) |
2020.0.x aka Ilford | 2.4.x, 2.5.x (Starting with 2020.0.3) |
Hoxton | 2.2.x, 2.3.x (Starting with SR5) |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
因此,我推荐使用:Spring Cloud 2021.0.x以及Spring Boot 2.7.x版本。
另外,Alibaba的微服务产品SpringCloudAlibaba目前也成为了SpringCloud组件中的一员,后面的内容中,我也会使用其中的部分组件。
二 微服务的拆分
2.1 熟悉我们的商城项目
先看一下我们商城的目录机构:
2.1.1 登录
看一下登录流程:
登录的方法在,controller。UserController这个类里面。
2.1.2 搜索商品
在首页的输入框里面,可以输入搜索关键字,点击搜索,进行搜索。
这个页面调用的接口是,/search/list,对应的服务端入口在,controller.SearchController这个类里面的Search方法。
2.1.3 购物车
在搜索到的商品列表中,点击按钮加入购物车
,即可将商品加入购物车:
加入成功后即可进入购物车列表页,查看自己购物车商品列表:
我们可以在这里进行删除和修改的操作
相关的功能在,controller.CartController中
其中,查询购物车列表时,由于要判断商品最新的价格和状态,所以还需要查询商品信息,业务流程如下:
2.1.4 下单
在购物车页面,点击结算按钮,会进入订单结算页面:
点击提交订单,会提交请求到服务端,服务端做3件事情:
-
创建一个新的订单
-
扣减商品库存
-
清理购物车中商品
业务入口在controller.OrderController类
中的createOrder
方法:
2.2 服务拆分原则
服务在才拆分的时候,我们要考虑几个问题:
什么时候拆?
怎么拆?
2.2.1 什么时候拆分
一般情况下,对于一个初创的项目,我们首先要做的就是验证项目的可行性。这个阶段,我们因该敏捷开发,快速生产可用的产品,投入市场进行验证。为了达到目的,我们往往是先开发单体架构,这个过程开发成本比较低,可以快速产出结果,一旦发现项目不符合市场,损失较小。
如果这一阶段采用复杂的微服务架构,投入大量的人力和时间成本用于架构设计,最终发现产品不符合市场需求,等于全部做了无用功。
最后得到的结论:
所以,对于大多数小型项目来说,一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构。这样初期成本会比较低,可以快速试错。但是,这么做的问题就在于后期做服务拆分时,可能会遇到很多代码耦合带来的问题,拆分比较困难(前易后难)。
而对于一些大型项目,在立项之初目的就很明确,为了长远考虑,在架构设计时就直接选择微服务架构。虽然前期投入较多,但后期就少了拆分服务的烦恼(前难后易)。
2.2.2 怎么拆分
微服务拆分时粒度要小,这其实是拆分的目标。具体可以从两个角度来分析:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。
高内聚首先是单一职责,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。
一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。
两种拆分方式
纵向拆分
横向拆分
所谓纵向拆分,就是按照项目的业务功能模块来拆分。例如商城中,就有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。
横向拆分是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。
我后面的微服务知识点,是按照纵向拆分的原则进行的
商品服务
购物车服务
用户服务
订单服务
支付服务
2.3 拆分商品服务,购物服务
一般微服务项目有两种不同的工程结构:
-
完全解耦:每一个微服务都创建为一个独立的工程,甚至可以使用不同的开发语言来开发,项目完全解耦。
-
优点:服务之间耦合度低
-
缺点:每个项目都有自己的独立仓库,管理起来比较麻烦
-
-
Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module
-
优点:项目代码集中,管理和运维方便(授课也方便)
-
缺点:服务之间耦合,编译时间较长
-
2.3.1 商品服务
1 创建模块
在hmall中创建新的module:
选择 New Module 输入名称 item-service;注意JDK版本要使用11
2,添加依赖
item-service的pom.xml文件参考如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.heima</groupId>
<artifactId>hmall</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>item-service</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在刚刚创建好的模块的pom,文件里面添加依赖。
3,创建启动引导类:
代码:
package com.hmall.item;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
@MapperScan("com.hmall.item.mapper")
@SpringBootApplication
public class ItemApplication {
public static void main(String[] args) {
SpringApplication.run(ItemApplication.class,args);
}
}
4,复制配置文件:
复制hm-service中的application.yml,application-local.yml,application-dev.yml,文件到item-service项目的resource下面。
修改application.yml文件修改如下:
server:
port: 8081
spring:
application:
name: item-service
profiles:
active: dev
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
global-config:
db-config:
update-strategy: not_null
id-type: auto
logging:
level:
com.hmall: debug
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"
knife4j:
enable: true
openapi:
title: 商品接口文档
description: "商品接口文档"
email: 666888.qq.com
concat: hhhhhh
url: https://www.123321.com
version: v1.0.0
group:
default:
group-name: default
api-rule: package
api-rule-resources:
- com.hmall.item.controller
hm:
jwt:
location: classpath:hmall.jks
alias: hmall
password: hmall123
tokenTTL: 30m
auth:
excludePaths:
- /search/**
- /users/login
- /items/**
- /hi
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
application-local.yaml里面也要改成自己虚拟机的IP地址和数据库密码
hm:
db:
host: 192.168.200.128 # 修改为你自己的虚拟机IP地址
pw: root # 修改为docker中的MySQL密码
5,复制代码:
复制hm-service中与商品有关的代码到item-service中,复制的代码参考如下:
小提示:
还有一个地方需要改进,ItemServiceImpl中的deductStock
方法:
代码:
@Override
public void deductStock(List<OrderDetailDTO> items) {
String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
boolean r = false;
try {
r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
} catch (Exception e) {
log.error("更新库存异常", e);
throw new BizIllegalException("库存不足!");
}
if (!r) {
throw new BizIllegalException("库存不足!");
}
}
这也是因为ItemMapper的所在包发生了变化,因此这里代码必须修改包路径。
6,创建数据库表:
最后我们还要导入数据库表。默认的数据库连接的是虚拟机里面的,我们在docker里面进行操作
hm-item.sql文件:
把这个脚本导入到虚拟机对应的数据库里面,创建数据库表:
找到我们的脚本文件:
看看刚才创建好的库
7,测试:
接下来就是启动项目进行测试了;复制一个启动项并修改如下:
看看配置项
启动测试一下下:
接着,启动item-service
,访问商品微服务的swagger接口文档:http://localhost:8081/doc.html
然后测试其中的根据id批量查询商品这个接口:
测试参数:100002672302,100002624500,100002533430,结果如下:
看到这个结果就代表我们的微服务拆分成功了
2.3.2 购物车服务
1,创建模块
下面就是刚刚创建好的模块
右击 cart-service
使用IDEA插件 JBLSpringBootAppGen
快速生成启动类及删除不相关文件。
2,添加依赖
打开 hmall/cart-service/pom.xml添加下面的依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.heima</groupId>
<artifactId>hmall</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>cart-service</artifactId>
<packaging>jar</packaging>
<name>cart-service</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3,修改启动引导类
@MapperScan("com.hmall.cart.mapper")
@SpringBootApplication
public class CartApplication {
public static void main(String[] args) {
SpringApplication.run(CartApplication.class,args);
}
}
4,复制配置文件
复制 hm-service
中的 application.yml、application-local.yml、application-dev.yml
文件到 item-service
项目的 resources
目录如下:
application.yml文件修改后如下:
server:
port: 8082
spring:
application:
name: cart-service
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
global-config:
db-config:
update-strategy: not_null
id-type: auto
logging:
level:
com.hmall: debug
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"
knife4j:
enable: true
openapi:
title: 商城接口文档
description: "商城接口文档"
email: 434343434@qq.com
concat: dsdsdsds
url: https://www.ffgfgfgf.cn
version: v1.0.0
group:
default:
group-name: default
api-rule: package
api-rule-resources:
- com.hmall.cart.controller
hm:
jwt:
location: classpath:hmall.jks
alias: hmall
password: hmall123
tokenTTL: 30m
auth:
excludePaths:
- /search/**
- /users/login
- /items/**
- /hi
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
5,复制代码
复制hm-service中与购物车有关的代码到cart-service中;复制代码参考如下
注意事项:
@Override
public List<CartVO> queryMyCarts() {
// 1.查询我的购物车列表
// TODO 先将用户的id写死为1
List<Cart> carts = lambdaQuery().eq(Cart::getUserId, 1L).list();
if (CollUtils.isEmpty(carts)) {
return CollUtils.emptyList();
}
// 2.转换VO
List<CartVO> vos = BeanUtils.copyList(carts, CartVO.class);
// 3.处理VO中的商品信息
handleCartItems(vos);
// 4.返回
return vos;
}
拆分好的结构
6,创建数据库表
在docker数据库中,执行下面的hm-cart的SQL。
运行后的结果
7,测试
在启动前,同样要配置启动项的active profile
为local
:
然后启动CartApplication
,访问swagger文档页面:http://localhost:8082/doc.html
看看测试结果:
我们注意到,其中与商品有关的字段值要么空要么是默认值!这就是因为刚才我们注释掉了查询购物车时,查询商品信息的相关代码。
那么,我们该如何在cart-service
服务中实现对item-service
服务的查询呢?
2.4服务调用
在拆分的时候,我们发现一个问题:就是购物车业务中需要查询商品信息,但商品信息查询的逻辑全部迁移到了item-service
服务,导致我们无法查询。
最终结果就是查询到的购物车数据不完整,因此要想解决这个问题,我们就必须改造其中的代码,把原本本地方法调用,改造成跨微服务的远程调用(RPC,即Remote Produce Call)。
我们查询购物车的流程就变成了这样的:
代码中需要变化的就是这一步:
那么问题来了:我们该如何跨服务调用,准确的说,如何在cart-service
中获取item-service
服务中的提供的商品数据呢?
我们之前是怎么实现,远程的调用呢
我们在Swagger测试商品查询接口,就是向http://localhost:8081/items这个接口发起请求。
而这种查询就是通过http请求的方式来完成的,不仅仅可以实现远程查询,还可以实现新增、删除等各种远程请求。
2.4.1 RestTemplate
Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送
其中提供了大量的方法,方便我们发送Http请求,例如:
查看 RestTemplate
类可以看到常见的Get、Post、Put、Delete请求都支持,如果请求参数比较复杂,还可以使用exchange方法来构造请求。
我们在cart-service服务中定义一个配置类:
代码:
@Configuration
public class RemoteCallConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.4.2 远程调用
从hm-service中复制,我们修改cart-service
中的com.hmall.cart.service.impl.CartServiceImpl
的handleCartItems
方法,发送http请求到item-service
:
private void handleCartItems(List<CartVO> vos) {
// 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
String itemUrl = "http://localhost:8081/items?ids={ids}";
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
itemUrl,//请求路径
HttpMethod.GET,//请求方式
null,//请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {
},//响应数据类型
Map.of("ids", CollUtils.join(itemIds, ","))//请求参数
);
List<ItemDTO> items = null;
if (response.getStatusCode().is2xxSuccessful()) {
items = response.getBody();
}
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}
我们重启cart-service,再次查询我们购物车列表接口
可以发现,所有商品相关数据都已经查询到了。
在这个过程中,item-service
提供了查询接口,cart-service
利用Http请求调用该接口。因此item-service
可以称为服务的提供者,而cart-service
则称为服务的消费者或服务调用者。
3,服务注册与发现
在上一章我们实现了微服务拆分,并且通过Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题。
试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图:
此时,每个item-service的实例其ip端口不同,问题来了。
-
item-service这么多实例,cart-service如何知道每一个实例的地址?
-
http请求要写url地址,
cart-service
服务到底该调用哪个实例呢? -
如果在运行过程中,某一个
item-service
实例宕机,cart-service
依然在调用该怎么办? -
如果并发太高,
item-service
临时多部署了N台实例,cart-service
如何知道新实例的地址?
为了解决上述问题,就必须引入注册中心的概念了,接下来我们就一起来分析下注册中心的原理。
3.1 注册中心原理
微服务远程调用的过程中,包括两个角色。
服务提供者:提供接口供其它微服务访问,比如
item-service
服务消费者:调用其它微服务提供的接口,比如
cart-service
在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:
流程如下:
1,服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
2,调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)
3,调用者自己对实例列表负载均衡,挑选一个实例
4,调用者向该实例发起远程调用
当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?
5,服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)
6,当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
7,当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
8,当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表
3.2 Nacos注册中心
3.2.1 注册中心简介
目前开源的注册中心框架有很多,国内比较常见的有:
-
Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用
-
Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用
-
Consul:HashiCorp公司出品,目前集成在SPringCloud中,不限制微服务语言
我就以国内使用较多的Nacos的学习来举例子
Nacos官方网站如下:
3.2.2 安装Nacos
1,创建Nacos数据库
我们基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以大家需要将资料中的 nacos.sql
SQL文件导入到你Docker中的MySQL容器中:
运行上述的SQL脚本文件之后;创建的nacos数据库表如下:
2,创建Nacos容器
修改文件中的 MYSQL_SERVICE_HOST
、MYSQL_SERVICE_PASSWORD
等,改为你自己虚拟机中的mysql容器对应的信息。
把资料下的/Nacos目录上传到虚拟机的/root目录。
进入root目录,然后执行下面的docker命令:
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--network hm-net \
--restart=always \
nacos/nacos-server:v2.1.0-slim
创建Nacos容器:
注意:如果下载 nacos 镜像有问题的话;则可使用资料提供的 nacos.tar
加载镜像的命令为:docker load -i nacos.tar
启动完成后,访问下面地址:http://192.168.200.128:8848/nacos/,注意将192.168.12.168
替换为你自己的虚拟机IP地址。首次访问会跳转到登录页,账号密码都是nacos