生产环境布置nacos及dubbo

Nacos,阿里巴巴提供的构建云本地应用的动态服务发现、配置中心和服务管理平台

可以查看Nacos官方文档

  1. 特性

    服务发现和管理

    动态服务发现是以服务为中心体系结构的关键。

    动态配置服务

    动态配置服务,外部化和动态的管理所有环境中的配置

    动态DNS

    支持加权路由,动态DNS服务在数据中心内的生产环境中间层负载均衡,灵活路由,流量控制、解析DNS服务,实现基于DNS的服务发现,最大程度降低耦合到特定于公司项目服务发现APi的风险

生产等级

  1. 阿里巴巴开源
  2. 支持数百万种服务的大规模方案
  3. 具有企业级SLA(服务级别协议)的开源产品

Nacos架构图

架构图

核心概念

  1. ==服务==

    特定信息的检索或一组操作的执行,其目的是不同的客户端可以为不同的目的重用,支持主流服务生态,K8s、RPC、spring cloud RESTful

  2. ==服务注册中心==

    服务实例及元数据的数据库,服务实例在启动时注册到服务注册表,在关闭的时候注销,服务和路由器的客户端查询服务注册表以查找服务的可用实例。服务注册中心可能会调用服务实例的健康检查API来验证它是否能够处理请求、

    *服务元数据 (Service Metadata)*

    服务元数据是指包括服务端点(endpoints)、服务标签、服务版本号、服务实例权重、路由规则、安全策略等描述服务的数据

    ** 服务提供方 (Service Provider)**

    是指提供可复用和可调用服务的应用方

    服务消费方 (Service Consumer)

    是指会发起对某个服务调用的应用方

    配置及配置管理省略…

    名字服务 (Naming Service)

    提供分布式系统中所有对象(Object)、实体(Entity)的“名字”到关联的元数据之间的映射管理服务,例如 ServiceName -> Endpoints Info, Distributed Lock Name -> Lock Owner/Status Info, DNS Domain Name -> IP List, 服务发现和 DNS 就是名字服务的2大场景。

    ** 配置服务 (Configuration Service)

    在服务或者应用运行过程中,提供动态配置或者元数据以及配置管理的服务提供者。

使用及部署

基于Docker安装Nacos

  1. 拉取镜像

    1
    docker pull nacos/nacos-server
  2. 创建目录及配置文件

    创建两个目录,分别是初始化目录和日志目录

    1
    mkdir -p/opt/nacos/init.d/opt/nacos/logs

    新建配置文件

    1
    vim /opt/nacos/init.d/custom.properties

    在文件中添加内容

    1
    management.endpoints.web.exposure.include=*

    创建并启动容器(默认端口8848)

    1
    省略: Mode= stangdalone(单机版)

    挂载配置文件和日志文件

    1
    --restart always 自启动容器

    使用MySQL数据库,配置—e变量

    配置

    1
    2
    单机为例:
    docker run -d -p 8848:8848 -p 9848:9848 -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /opt/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties -v /opt/nacos/logs:/home/nacos/logs --restart always --name nacos nacos/nacos-server

    注意: Nacos服务器的版本如果是2.0.x。需要额外绑定端口 9849。否则无法实现服务的注册与发现。因为nacos服务器版本是2.0.x时,要求服务注册时,检查gRPC端口,此端口为9848,且端口无法访问时,服务启动抛出异常,因为nacos-discovery连接nacos-server的时候,gRPC端口无法访问,认为当前服务注册失败。

启动Nacos,访问可视化页面

访问:http://ip:8848/nacos

默认账号密码为nacos

使用Nacos做注册中心注意

Nacos支持HTTP和RPC协议访问。在导入Nacos依赖后内置Ribbon。和Eureka相同,都支持同名应用程序的负载均衡。

所以在使用Nacos作为注册中心的时候除了和Eureka依赖及配置文件不一样,其他流程都是一样的。

注意:Spring Cloud Alibaba依赖的Spring Boot最高版本只能是2.3.x。更高版本的spring boot和 spring cloud alibaba有兼容错误。启动失败。

注意:spring cloud alibaba 2021版本依赖,在使用openfeign技术做远程服务调用的时候,需要spring boot2.4.2及更高版本中的loadbalancer支持。但是启动的时候,必须使用spring boot 2.3.x版本中的spring-boot-context才能启动。有依赖冲突。无法实现服务的正常发现。暂时使用spring cloud alibaba2.2.x版本可以正常基于openfeign实现远程服务调用。

导入项目pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

父项目下新建Provider,配置依赖

1
2
3
4
5
6
7
8
9
10
<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>
</dependencies>

编写配置文件

新建APPlocation.yml

server-addr:配置nacos的ip和端口。

1
2
3
4
5
6
7
8
9
server:
port: 8080
spring:
application:
name: provider
cloud:
nacos:
discovery:
server-addr: ip:8848

新建控制器

新建com.dffy.controller.ProviderController

该控制器是为了让另一个项目调用的。

1
2
3
4
5
6
7
@RestController
public class ProviderController {
@RequestMapping("/provider")
public String provider(){
return "provider-controller";
}
}

新建启动类

新建com.dffy.ProviderApplication

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}

启动项目

启动项目中可以在Nacos控制台中发现注册的信息

新建Consumer项目

在父项目下新建及配置依赖

1
2
3
4
5
6
7
8
9
10
<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>
</dependencies>

新建配置文件

在resources下新建application.yml

端口和应用程序名不可重复

1
2
3
4
5
6
7
8
9
server:
port: 8081
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: ip:8848

新建配置类

新建com.dffy.config.ConsumerConfig

1
2
3
4
5
6
7
8
@Configuration
public class ConsumerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}

新建service及实现类

http://provider/provider 中第一个provider是另一个项目的应用程序名。第二个provider是控制器名称。

1
2
3
public interface ConsumerService {
String consumer();
}
1
2
3
4
5
6
7
8
9
10
11
@Service
public class ConsumerServiceImpl implements ConsumerService {
@Autowired
private RestTemplate restTemplate;

@Override
public String consumer() {
String result = restTemplate.getForObject("http://provider/provider", String.class);
return result;
}
}

新建控制器

1
2
3
4
5
6
7
8
9
10
@RestController
public class ConsumerController {
@Autowired
private ConsumerService consumerService;

@RequestMapping("/consumer")
public String consumer(){
return consumerService.consumer();
}
}

新建启动类ConsumerApplication

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}

运行项目,是否成功注册

测试服务调用

浏览器输入ip:8081/consumer

使用Nacos作为Dubbo的注册中心

新建父项目

配置pom依赖

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
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>

新建API子项目

新建项目api.

此项目作为Dubbo发布接口的项目

新建接口

新建.api.DemoApiService(不能和Consumer的service接口名重复)

1
2
3
public interface DemoApiService {
String demo();
}

新建Provider子项目

配置pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
<dependency>
<artifactId>api</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
</dependencies>

新建配置文件

新建application.yml

dubbo.scan.backages:扫描@DubboService所在包

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
dubbo:
# scan:
# base-packages: com.flysoft.service.impl
# cloud:
# subscribed-services: dubbo-provider # 订阅的服务,默认 *
protocol:
# dubbo 协议
name: dubbo
# dubbo 协议端口( -1 表示自增端口,从 20880 开始)
port: -1
registry:
# 挂载到注册中心
address: nacos://ip:8848
spring:
application:
# Dubbo 应用名称
name: dubbo-provider
# main:
# Spring Boot 2.1 需要设定
# allow-bean-definition-overriding: true
cloud:
nacos:
# Nacos 服务发现与注册配置
discovery:
server-addr: ip:8848

新建实现类

service.impl.DemoServiceImpl

1
2
3
4
5
6
7
@DubboService
public class DemoServiceImpl implements DemoApiService {
@Override
public String demo() {
return "dubbo-provider-service";
}
}

新建启动类

.ProviderApplication

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
@EnableDubbo
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}

新建Consumer项目

配置pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
<dependency>
<artifactId>api</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
</dependencies>

新建配置文件application.yml

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
dubbo:
registry:
# 挂载到 Spring Cloud 注册中心
address: nacos://ip:8848
# cloud:
# subscribed-services: dubbo-provider
protocol:
# dubbo 协议
name: dubbo
# dubbo 协议端口( -1 表示自增端口,从 20880 开始)
port: -1
server:
port: 8081
spring:
application:
# Dubbo 应用名称
name: dubbo-consumer
# main:
# Spring Boot 2.1 需要设定
# allow-bean-definition-overriding: true
cloud:
nacos:
# Nacos 服务发现与注册配置
discovery:
server-addr: ip:8848

新建service及实现类

service.ConsumerService

1
2
3
public interface ConsumerService {
String consumer();
}
1
2
3
4
5
6
7
8
9
@Service
public class ConsumerServiceImpl implements ConsumerService {
@DubboReference
private DemoApiService demoApiService;
@Override
public String consumer() {
return demoApiService.demo();
}
}

新建控制器

.controller.ConsumerController

1
2
3
4
5
6
7
8
9
10
@RestController
public class ConsumerController {
@Autowired
private ConsumerService consumerService;

@RequestMapping("/consumer")
public String consumer(){
return consumerService.consumer();
}
}

新建启动类

.ConsumerApplication

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}

基于OpenFeign实现远程服务调用

修改pom

增加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

定义OpenFeign调用接口

1
2
3
4
5
@FeignClient("dubbo-provider")
public interface CallProviderInterface {
@RequestMapping("/provider")
String test();
}

修改控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class ConsumerController {
@Autowired
private ConsumerService consumerService;

@Autowired
private CallProviderInterface callProviderInterface;

@RequestMapping("/consumer2")
public String consumer(){
return callProviderInterface.test();
}

@RequestMapping("/consumer")
public String consumer(){
return consumerService.consumer();
}
}

修改启动类

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}

测试

实现Nacos作为配置中心

在Nacos中新建配置文件

访问可视化

配置列表点击+

添加配置文件信息

Data ID:名称任意。此名称作为配置文件名称(又可以叫前缀(prefix))

Group: 在没有自己建立组时默认为DEFAUL_GROUP。此值不需要修改

配置格式:选择properties或yml。此配置影响到客户端加载配置文件时配置。

配置内容:根据所选配置格式,按照指定格式填写内容。

点击发布按钮后弹出配置文件信息

点击确定按钮不会自动关闭这个页面。而是回到编辑视图

在配置列表中查看

新建项目

任意名称的项目

编写pom

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
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-dependencies</artifactId>-->
<!-- <version>Hoxton.SR8</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

编写配置文件

新建配置文件

新建bootstarap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8070
spring:
cloud:
nacos:
discovery:
server-addr: ip:8848
config: # 指定配置中心的地址和配置中心使用的数据格式
server-addr: ip:8848
# 默认加载文件扩展名为properties
file-extension: properties
# group默认值为DEFAULT_GROUP
# group: DEFAULT_GROUP
# 加载的配置文件名称默认值为应用程序名

# prefix: ${spring.application.name}
application:
name: config-client

新建service及实现类

.service.ConfigClientService及实现类

Nacos支持动态刷新功能。在获取分布式配置中心内容的类上添加@RefreshScope可以热刷新

1
2
3
public interface ConfigClientService {
String configClient();
}
1
2
3
4
5
6
7
8
9
10
11
12
@Service
@RefreshScope
public class ConfigClientServiceImpl implements ConfigClientService {
@Value("${my.name}")
private String name;
@Value("${my.age}")
private Integer age;
@Override
public String configClient() {
return name+"---"+age;
}
}

新建控制器

.controller.ConfigServerController

1
2
3
4
5
6
7
8
9
10
@RestController
public class ConfigServerController {
@Autowired
private ConfigClientService configClientService;

@RequestMapping("/")
public String configServer(){
return configClientService.configClient();
}
}

新建启动类

.ConfigClientApplication

1
2
3
4
5
6
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class,args);
}
}

测试