微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1

Posted ShuSheng007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1相关的知识,希望对你有一定的参考价值。

[版权申明] 非商业目的注明出处可自由转载
出自:shusheng007

概述

在上一篇微服务之如何从零搭建(吹牛逼篇)中概述了当前Java生态中从零搭建微服务架构所涉及的一些技术和组件,接下来我会选择当前较为流行的组件逐步搭建一套演示微服务架构,请有兴趣的同学持续关注。

由于微服务架构中服务天生就是要随时准备着生死,这就使得服务注册与发现成为一个非常关键的功能。阿里开源的Nacos就是其中的佼佼者,详情请参考官方文档,我们今天就实际上手一下这个组件。

安装Nacos

我们知道Nacos会作为一个服务(集群)运行,然后我们的其他微服务与其通信来注册自己和发现其他服务,所以首先我们肯定需要将其运行起来。容器化的浪潮加速了云时代的带来,未来的世界必然是一个云的世界,我们这里也准备采用docker来部署nacos。

安装Docker环境

首先确认你的电脑是否已经安装了Docker。打开终端或命令行,输入如下命令

docker --version

如果可以正常显示出类似如下输出说明你本机安装了docker,否则请参考官方文档完成安装。

Docker version 20.10.8, build 3967b7d

运行nacos

Nacos 在Docker Hub的下载地址为:https://hub.docker.com/r/nacos/nacos-server

打开终端,输入如下命令

docker run --name nacos-quick -e MODE=standalone -p 8849:8848 -d nacos/nacos-server:2.0.2

docker会自动下载并运行nacos,上述命令中各参数的含义如下:

--name nacos-quick         设置容器的名称为nacos-quick
-e MODE=standalone         以单机模式启动
-p 8849:8848               外部端口为8849,内部端口为8848
-d                         以detach模式启动,类似于使容器运行于后台,当你关闭终端后容器也会继续运行
nacos/nacos-server:2.0.2   nacos在docker hub的地址

接下来使用如下命令查看正在运行的container

docker ps 

下面是我电脑的输出,可以发现nacos已经运行起来了。

CONTAINER ID     IMAGE                      COMMAND                   CREATED       STATUS             PORTS                                         NAMES
6c32be7e7d33     nacos/nacos-server:2.0.2   "bin/docker-startup.…"   3 weeks ago   Up 5 seconds    0.0.0.0:8849->8848/tcp, :::8849->8848/tcp       nacos-quick

UI查看Nacos状态

Nacos还很贴心的为我们准备了一个web UI界面,通过此UI界面我们就可以清楚的看到当前注册服务的状态。当Nacos容器正常运行后,通过浏览器访问 http://127.0.0.1:8849/nacos查看,初始用户名和密码都是nacos,一切正常的话会显示出下面的界面。


至此,Nacos服务已经成功运行起来了,接下来我们就使用SpringCloud开发几个微服务来使用nacos。

在SpringCloud中使用

因为Nacos属于阿里巴巴开源产品,而阿里巴巴已经拥抱了SpringCloud,因此此组件也被集成到了SpringCloud中,因此我们可以很方便的在SpringCloud中使用它。我们准备新建两个微服务,一个将地址注册到Nacos,另一个通过Nacos获取其地址后发起调用。

我们新建一个名为master-microservice的maven项目,其他微服务作为它的module。

新建一个Maven父项目

项目结构如下图,你可以在文末获取到项目的源代码

pom.xml关键内容如下

<project ...>
    <modelVersion>4.0.0</modelVersion>

    <groupId>top.shusheng007</groupId>
    <artifactId>master-microservice</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>goodsServer</module>
        <module>orderServer</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
...
        <spring-cloud.version>2020.0.4</spring-cloud.version>
        <spring.cloud.alibaba.version>2021.1</spring.cloud.alibaba.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        ...
    </dependencies>

    <dependencyManagement>
        <dependencies>
<!--            SpringCloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>$spring-cloud.version</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
<!--            SpringCloud Alibaba依赖-->
            <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>

</project>

我们可以看到,这个父项目引入了SpringCloud与SpringCloud Alibaba两个依赖族。注意他们之间的版本是有依赖的,不能瞎搞,例如本文中spring.cloud.alibaba 2021.1适配了spring-cloud 2020.0.4。至于版本对应详情,可参考SpringCloudAlibaba官网。

当引入了前两个依赖族后我们就可以通过下面的代码引入nacos相关的依赖了。

 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>

创建一个订单服务(OrderService)

此微服务负责订单领域相关的业务,我们会将其注册到Nacos中去。那么具体如何操作呢?

第一步: 创建一个SpringBoot项目

此项目作为master-microservice的Module,这样我们就可以使用其引入的各种依赖了,就不用在每个微服务中重复引入依赖。

pom.xml中关键配置如下:

    <parent>
        <groupId>top.shusheng007</groupId>
        <artifactId>master-microservice</artifactId>
        <version>1.0</version>
    </parent>

由于我们在其父项目中引入了nacos相关的依赖,所以加入此配置后就可以直接在OrderService项目中使用了。

此服务中提供一了个支付订单的API,后面我们使用GoodsService微服务来消费这个接口

@RestController
@RequestMapping("/order")
public class OrderController 
    private final OrderService orderService;
    ...
    @PostMapping(value = "/payment")
    public OrderDetail payment(@RequestBody PaymentReq paymentReq)
        return orderService.paymentOrder(paymentReq.getOrderId());
    

第二步: 配置nacos连接信息

application.yaml文件中配置OrderService

server:
  port: 9081

spring:
  application:
    name: order-service
  # nacos 相关配置
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8849

可以看到,我们配置了OrderService的名称、端口号以及nacos的地址

第三步: 启用nacos

由于SpringCloud集成了Nacos,所以使得启用Nacos异常简单,只需要一个注解

OrderServerApplication上添加@EnableDiscoveryClient即可

@SpringBootApplication
@EnableDiscoveryClient
public class OrderServerApplication 
    public static void main(String[] args) 
        SpringApplication.run(OrderServerApplication.class, args);
    

测试结果

通过以上3步就完成了OrderService的创建,让我们来测试一下是否一切正常吧。

  • 启动前状态

通过Nacos UI界面( http://127.0.0.1:8849/nacos)看一下启动前的状态,可以发现没有服务注册

  • 启动后状态

启动OrderService,然后刷新一下,可以发现已经有一个order-service实例启动了。


当前微服务架构为了实现服务的高可用性及伸缩性,以集群部署服务已经成为常态,所以这里我们也再启动几个实例看看效果。那么我们如何在Intelij中启动多个实例呢?

如何在Intelij里启动同一个应用的多个实例?

  • 打开Edit Configurations 的弹窗
  • 选中你要启动的应用,然后勾选右边的 Allow parallel run选项,
  • 在启动参数中配置你要启动实例的端口,例如此处的-Dserver.port=9082
  • 点击apply按钮,点击ok按钮

  • 点击运行按钮,一个新实例就会启动

  • 启动第二个时,还是进入 Edit Configuration 弹窗,修改在启动参数中将端口修改为另一个值,确定后再次点击运行按钮,如下图所示。


当我们按照上面的方法启动第二个实例后,从nacos UI上查看可以发现确实多了一个实例

创建一个商品服务(GoodsService)

上面我们已经创建并启动了OrderService服务的两个实例,接下来让我们再创建一个商品服务来消费它们。

第一步: 创建一个SpringBoot项目

负责商品领域相关的业务,结构与OrderService类似,不再赘述。

第二步: 配置服务及Nacos

application.yaml中配置服务的名称、端口号以及nacos的地址。

server:
  port: 9091

spring:
  application:
    name: goods-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8849

GoodsServerApplication上加上@EnableDiscoveryClient注解

第三步: 消费OrderService服务

由于OrderService启动了多个对等实例(集群),所以GoodsService需要使用负载均衡来消费这些实例。

  • 首先引入负载均衡依赖
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-loadbalancer</artifactId>
 </dependency>
  • 配置WebClient

此处我们使用了WebClient而非RestTemplate来发起网络请求,所以要给WebClient配置负责均衡。

@Configuration
public class LoadBalanceConfiguration 
    @LoadBalanced
    @Bean
    public WebClient.Builder loadBalancedWebClientBuilder()
        return WebClient.builder();
    

最关键的就是属性:@LoadBalanced

接下来我们就可以使用WebClient来对OrderService发起请求了,如下代码所示。注意那个调用路径(URL): http://order-service/order/payment,要使用目标服务的服务名称,而非域名或者IP地址。

@Service
public class GoodsServiceImpl implements GoodsService 
    @Override
    public Mono<String> buyBook(String bookId) 
        return webClientBuilder.build().post()
                .uri("http://order-service/order/payment")
                .body(Mono.just(new PaymentReq(bookId)), PaymentReq.class)
                .retrieve()
                .bodyToMono(String.class);
    


@RestController()
@RequestMapping("/goods")
public class GoodsController 
     private final GoodsService goodsService;
	 ...
    //调用order-service微服务,通过服务发现获取到地址信息后调用服务实例
    @GetMapping("/buyBook/bookId")
    public Mono<String> buyBook(@PathVariable String bookId)
        return goodsService.buyBook(bookId);
    
 

调用

使用PostMan或者浏览器向http://127.0.0.1:9091/goods/buyBook/1多次发起Get请求

输出如下日志:

[goods-service,,] 51158 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [27f709e5-3, L:/0:0:0:0:0:0:0:1:9091 - R:/0:0:0:0:0:0:0:1:53608] HTTP GET "/goods/buyBook/1"
...
[7d963e3a] HTTP POST http://10.68.180.105:9082/order/payment

...
[goods-service,,] 51158 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [27f709e5-4, L:/0:0:0:0:0:0:0:1:9091 - R:/0:0:0:0:0:0:0:1:53608] HTTP GET "/goods/buyBook/1"
...
[25d4a77d] HTTP POST http://10.68.180.105:9081/order/payment
...

从日志可见,9081与9082两个服务实例交替被调用。为什么是交替被调用呢?这个是由客户端的负载均衡算法决定,由于SpringCloud默认使用round-robin算法,所以这里就是交替进行。

总结

自此,我们已经成功构建了两个微服务,并使用Nacos作为服务的注册与发现组件使他们进行了通信,其中涉及到了负载均衡的使用。但在本文我们只是对其浅尝辄止,由于其在现在软件架构中的重要性,我会继续完善我们这个demo工程中的负载均衡部分,并使用单独文章做介绍,请持续关注。

源代码GitHub地址:本文首发末尾

以上是关于微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1的主要内容,如果未能解决你的问题,请参考以下文章

云原生系列 | 微服务配置中心之 Nacos

微服务实践之网关(Spring Cloud Gateway)详解-SpringCloud(2021.0.x)-3

微服务实践之负载均衡(Spring Cloud Load Balancer)-SpringCloud(2020.0.x)-2

物联网架构成长之路(51)-Nacos微服务配置中心服务注册服务发现

3. nacos服务发现

nacos微服务(一)