微服务实践之服务注册与发现(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(2020.0.x)-1_第1张图片
至此,Nacos服务已经成功运行起来了,接下来我们就使用SpringCloud开发几个微服务来使用nacos。

在SpringCloud中使用

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

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

新建一个Maven父项目

项目结构如下图,你可以在文末获取到项目的源代码
微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第2张图片
pom.xml关键内容如下


    4.0.0

    top.shusheng007
    master-microservice
    1.0
    pom

    
        goodsServer
        orderServer
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.5.6
         
    

    
...
        2020.0.4
        2021.1
    

    
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        ...
    

    
        

            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            

            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring.cloud.alibaba.version}
                pom
                import
            
        
    


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

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

 
     com.alibaba.cloud
     spring-cloud-starter-alibaba-nacos-discovery
 

创建一个订单服务(OrderService)

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

第一步: 创建一个SpringBoot项目

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

pom.xml中关键配置如下:

    
        top.shusheng007
        master-microservice
        1.0
    

由于我们在其父项目中引入了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)看一下启动前的状态,可以发现没有服务注册
微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第3张图片

  • 启动后状态

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

微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第4张图片
当前微服务架构为了实现服务的高可用性及伸缩性,以集群部署服务已经成为常态,所以这里我们也再启动几个实例看看效果。那么我们如何在Intelij中启动多个实例呢?

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

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

微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第5张图片

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

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

在这里插入图片描述
当我们按照上面的方法启动第二个实例后,从nacos UI上查看可以发现确实多了一个实例

微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第6张图片

创建一个商品服务(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需要使用负载均衡来消费这些实例。

  • 首先引入负载均衡依赖
 
     org.springframework.cloud
     spring-cloud-starter-loadbalancer
 
  • 配置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 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 buyBook(@PathVariable String bookId){
        return goodsService.buyBook(bookId);
    }
 }

调用

使用PostMan或者浏览器向http://127.0.0.1:9091/goods/buyBook/1多次发起Get请求
微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1_第7张图片

输出如下日志:

[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地址:本文首发末尾

你可能感兴趣的