云原生=DevOps+CI/CD+容器化
dns,vip(虚拟ip),address-server
支持,http,dns,udp,tls
就能使用 ConfigService 和 NamingService(服务发现) 两大核心功能nacos.core.auth.enabled=true #开启鉴权
nacos.core.auth.server.identity.key=nacosIdKey #设key值
nacos.core.auth.server.identity.value=nacosIdValue #设value值
### The default token (Base64 String):
nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
执行 startup.cmd -m standalone
指定单机启动,默认是集群启动<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cheese</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>springcloudalibaba</name>
<description>springcloudalibaba</description>
<modules>
<module>01-provider-nacos-8081</module>
<module>02-consumer-nacos-8080</module>
<module>03-provider-config-8081</module>
<module>04-consumer-openfegin-8080</module>
</modules>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
</properties>
<!--管理子工程的springcloudalibaba版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
<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>
<!--指定父工程gav-->
<parent>
<groupId>com.cheese</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>01-provider-nacos-8081</artifactId>
<packaging>jar</packaging>
<name>01-provider-nacos-8081</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<!--外部依赖需要指定version-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.23</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 8081
spring:
#微服务名称
application:
name: depart-provider
spring:
jpa:
generate-ddl: true #指定spring容器启动时创建表,默认为false
show-sql: true #sql是否在控制台显示sql语句,默认为fasle
hibernate:
ddl-auto: none #设置应用重启时不更新表
-- auto-generated definition
create schema alibaba2024 collate utf8mb4_0900_ai_ci;
use alibaba2024;
-- auto-generated definition
create table depart_entity
(
id int not null
primary key,
name varchar(255) null
);
@Data
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer","handler","fieldHandler"})
/**
* JPA查询对象,默认延迟加载,忽略延迟加载
*/
public class DepartEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)//自增ID
public Integer id;
public String name;
}
@RequestMapping("/provider/depart")
@RestController
public class DepartController {
@Autowired
private DepartService departService;
/**
* 一般生产环境中会采用同一个接口名称,CRUD通请求方式进行区分
* @param depart
* @return
*/
//新增
@PostMapping("/save")
public Boolean addDepart(@RequestBody DepartEntity depart) {
return departService.addDepart(depart);
}
//删除
@DeleteMapping("/del/{id}")
public Boolean deleteDepart(@PathVariable("id") Integer id) {
return departService.deleteDepartById(id);
}
//修改
@PutMapping("/update")
public Boolean updateDepart(@RequestBody DepartEntity depart) {
return departService.updateDepart(depart);
}
//获取
@GetMapping("/get/{id}")
public DepartEntity getDepart(@PathVariable("id") Integer id) {
return departService.getDepartById(id);
}
//列表
@GetMapping("/list")
public List<DepartEntity> getDepartList() {
return departService.getAllDepart();
}
}
public interface DepartService {
//insert
boolean addDepart(DepartEntity depart);
//update
boolean updateDepart(DepartEntity depart);
//delete
boolean deleteDepartById(Integer id);
//get
DepartEntity getDepartById(Integer id);
//Query
List<DepartEntity> getAllDepart();
}
@Service
public class DepartServiceImpl implements DepartService {
@Resource
private DepartRepository departRepository;
@Override
public boolean addDepart(DepartEntity depart) {
return departRepository.save(depart) != null;
}
@Override
public boolean updateDepart(DepartEntity depart) {
return addDepart(depart);//JPA底层使用了saveOrUpdate方法区别在于传入的对象ID属性是否null
}
@Override
public boolean deleteDepartById(Integer id) {
if (departRepository.existsById(id)) {
departRepository.deleteById(id);
return true;
}
return false;
}
@Override
public DepartEntity getDepartById(Integer id) {
if (departRepository.existsById(id)) {
return departRepository.getReferenceById(id);
}
DepartEntity depart = new DepartEntity();
depart.setName("not exist this depart");
return depart;
}
@Override
public List<DepartEntity> getAllDepart() {
return departRepository.findAll();
}
}
public interface DepartRepository extends JpaRepository<DepartEntity, Integer> {
}
# 端口号
server:
port: 8080
spring:
#微服务名称
application:
name: depart-consumer
@Configuration
public class DepartConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
@RequestMapping("/consumer/depart")
public class DeportController {
@Resource
private RestTemplate restTemplate;
//直连方式
public static final String SERVICE_PROVIDER = "http://localhost:8081/provider/depart";
//新增
@PostMapping("/")
public Boolean addDepart(@RequestBody DepartEntity depart) {
return restTemplate.postForObject(SERVICE_PROVIDER + "/save", depart, Boolean.class);
}
//修改
@PutMapping("/")
public void modifyDepart(@RequestBody DepartEntity depart) {
restTemplate.put(SERVICE_PROVIDER + "/update", depart);
}
//删除
@DeleteMapping("/{id}")
public void deleteDepart(@PathVariable Integer id) {
restTemplate.delete(SERVICE_PROVIDER + "/del/" + id);
}
//查询
@GetMapping("/{id}")
public DepartEntity getDepart(@PathVariable Integer id) {
return restTemplate.getForObject(SERVICE_PROVIDER + "/get/" + id, DepartEntity.class);
}
//列表
@GetMapping("/list")
public List<DepartEntity> getDepartList() {
return restTemplate.getForObject(SERVICE_PROVIDER + "/list", List.class);
}
}
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos注册中心地址
username: nacos
password: nacos
//直连方式
//public static final String SERVICE_PROVIDER = "http://localhost:8081/provider/depart";
//微服务调用方式
public static final String SERVICE_PROVIDER = "http://depart-provider/provider/depart";
注意:在 nacos2.2.0.1 版本以后摈弃了 ribbon 客户端负载均衡器,使用了 springcloud 社区的 loadbalancer,consumer 中需要新增如下依赖
<!--nacos2.2.0.1往后版本的客户端负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
@Configuration
public class DepartConfig {
@Bean
//负载均很方式调用,Netflix组件中的负载均衡在nacos中不再使用,
//nacos使用新的依赖spring-cloud-starter-loadbalancer实现客户端负载均衡
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
depart-provider
的微服务
@RequestMapping("/provider/depart")
@RestController
public class DepartController {
.....
@Autowired
private DiscoveryClient discoveryClient; //服务发现客户端
@GetMapping("/discovery")
public List<String> discoveryHandle() {
List<String> services = discoveryClient.getServices();
services.forEach(v1->{
//获取微服务名称的所有微服务实例
discoveryClient.getInstances(v1).forEach(v2->{
HashMap<String, Object> map = new HashMap<>();
map.put("serviceName", v1);
map.put("serviceId", v2.getServiceId());
map.put("serviceHost", v2.getHost());
map.put("servicePort", v2.getPort());
map.put("uri", v2.getUri());
System.out.println("map = " + map);
});
});
return services; //返回注册中心中所有活跃的微服务名称列表
}
.......
}
spring:
cloud:
nacos:
discovery:
ephemeral: false #设置是否临时节点,默认为true,为临时节点,false,设置不为临时节点,即为持久实例,已注册的临时实例不可修改为持久实例
curl -d 'serviceName=depart-provider' \
-d 'ip=192.168.1.102' \
-d 'port=8081' \
-d 'ephemeral=false' \
-d 'username=nacos' \
-d 'password=nacos' \
-X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance'
默认情况下,Nacos Discovery集群的数据一致性采用的是 AP 模式。但其也支持 CP 模式,需要进行转换。若要转换为CP 的,可以提交如下 PUT 请求,完成 AP 到 CP 的转换。
nacos-server-2.2.1\nacos\conf
路径下create database nacos_config
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced.
spring.datasource.platform=mysql
spring.sql.init.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=abc123
cluster.conf
#端口号不可连续避免系统端口被占用
192.168.1.102:8849
192.168.1.102:8851
192.168.1.102:8853
startup.cmd
默认集群启动spring:
cloud:
nacos:
discovery:
#server-addr: localhost:8848 #nacos注册中心地址
#集群注册
server-addr: localhost:8849,localhost:8851,localhost:8853 #nacos注册中心地址
username: dev
password: dev01
无论采用何种部署方式,推荐用户把Nacos集群中所有服务节点放到一个vip下面,然后挂到一个域名下面。
<http://ip1:port/openAPI>
直连ip模式,机器挂则需要修改ip才可以使用。<http://SLB:port/openAPI>
挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。<http://nacos.com:port/openAPI>
域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式
在使用VIP时,需要开放Nacos服务的主端口(默认8848)以及gRPC端口(默认9848)、同时如果对Nacos的主端口有所修改的话,需要对vip中的端口映射作出配置,具体端口的映射方式参考部署手册概览-Nacos部署架构
Nacos 中的服务是由三元组唯一确定的:namespace、group 与服务名称 service。namespace 与 group 的作用是相同的,用于划分不同的区域范围,隔离服务。不同的是,namespace 的范围更大,不同的 namespace 中可以包含相同的group。不同的 group 中可以包含相同的 service。namespace 的默认值为 public,group 的默认值为 DEFAULT GROUP。。它们之间的关系就如官方给出的下图所示。
启动三个 provider-nacos-8081 实例,提供相同的服务,不同的 namespace,group,和 port
命名空间 | 分组名称 | 端口号 |
---|---|---|
public | DEFAULT_GROUP | 8081 |
public | MY_GROUP | 8082 |
ns_test | MY_GROUP | 8083 |
@Service
public class DepartServiceImpl implements DepartService {
@Resource
private DepartRepository departRepository;
//获取端口号
@Value("${server.port}")
private Integer port;
@Override
public DepartEntity getDepartById(Integer id) {
if (departRepository.existsById(id)) {
return departRepository.getReferenceById(id);
}
DepartEntity depart = new DepartEntity();
depart.setName("not exist this depart port: "+ port);
return depart;
}
}
集群中每一台主机的配置文件都是相同的,对配置文件的更新维护就成为了一个棘手的问题。此时就出现了配置中心,将集群中每个节点的配置文件交由配置中心统一管理。相关产品很多,例如,Spring Cloud config、Zookeeper、Apollo、Disconf等。但 Spring Cloud Alibaba官方推荐使用 Nacos 作为微服务的配置中心。”
其存在三大问题:
其 Config client 可以自动感知配置文件的更新。但也存在两个不足:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
bootstrap.yaml
文件,新版 nacos 本地配置文件任然为 application.yaml
spring:
#微服务名称
application:
name: depart-provider
cloud:
nacos:
config:
server-addr: localhost:8848
username: nacos
password: nacos
spring:
cloud:
nacos:
config:
file-extension: yaml #读取的配置文件类型
spring:
# 新版新增配置
config:
import:
- optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
spring:
cloud:
nacos:
config:
#实现共享配置,前置条件,同一个namespace下的同一个group的不同服务共享
shared-configs[0]:
data-id: shareconfig.yaml
refresh: true
shared-configs[1]:
data-id: shareconfig2.yaml
refresh: true
#实现拓展配置,不同namespace,不同group均可共享
extension-configs[0]:
data-id: extensionconfig.yaml
refresh: true
extension-configs[1]:
data-id: extensionconfig2.yaml
refresh: true
# 配置加载顺序: 共享配置=>扩展配置=>当前配置
# 配置优先级: 后加载的会覆盖先加载的=> 当前配置>扩展配置>共享配置
这三个同名文件也存在加载顺序问题,它们的加载顺序为:本地配置文件、远程配置文件、快照配置文件。只要系统加载到了配置文件,那么后面的就不再加载。
@Service
@RefreshScope //实现动态刷新
public class DepartServiceImpl implements DepartService {
@Resource
private DepartRepository departRepository;
//获取端口号
@Value("${server.port}")
private Integer port;
//获取部门名称通过远程配置中心
@Value("${depart.name}")
private String departName;
@Override
public DepartEntity getDepartById(Integer id) {
if (departRepository.existsById(id)) {
return departRepository.getReferenceById(id);
}
DepartEntity depart = new DepartEntity();
depart.setName("not exist this depart"+departName+" port: "+ port);
return depart;
}
}
在开发应用时,通常同一套程序会被运行在多个不同的环境,例如,开发、测试、生产环境等。每个环境的数据库地址、服务器端口号等配置都会不同。若在不同环境下运行时将配置文件修改为不同内容,那么,这种做法不仅非常繁琐,而且很容易发生错误。此时就需要定义出不同的配置信息,在不同的环境中选择不同的配置。
depart-provider.yaml
进行克隆application.yaml
文件中做以下配置spring:
profiles:
active: dev
# 新版新增配置
config:
import:
- optional:nacos:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
启动三个 provider-config-8081 实例,提供相同的服务,不同的 namespace,group,和 port
命名空间 | 分组名称 | 端口号 |
---|---|---|
public | DEFAULT_GROUP | 8081 |
public | MY_GROUP | 8082 |
ns_test | MY_GROUP | 8083 |