SpringCloud快速入门(4)---- nacos安装与使用

在微服务架构中,会有很多服务(订单服务、用户服务、支付服务),服务之间需要互相调用。Nacos就是注册中心,所有服务启动后,都会主动把自己的IP、端口注册到Nacos中。当A服务需要调用B服务时,直接去Nacos查询B服务的地址即可,无需硬编码写死地址。

1. nacos安装与启动

进入nacos官网点击下载对应版本:
发布历史 | Nacos 官网

下载成功后解压:

在bin目录打开cmd窗口,输入启动指令:startup.cmd -m standalone

这里的m是model模式,standalone代表单机

启动成功后我们访问:localhost:8848/nacos 即可进入

2. 服务注册

2.1 配置nacos地址

上期我们已经创建了两个服务,我们给他们添加启动类运行起来:

添加springweb依赖

创建完启动类之后我们还需要在配置文件中配置nacos的地址:

复制代码
spring.application.name=servers-order
server.port=8000

#配置nacos地址
spring.cloud.nacos.server-addr=127.0.0.1:8848

随后我们启动这个服务就可以在nacos的可视化界面看到这个服务,说明注册成功:

同理注册其它服务也是相同的方式

2.2 配置集群与分组

服务分组:用来对不同环境 / 业务线的服务做隔离,不同分组的服务默认无法互相发现。适用场景:开发 / 测试 / 环境隔离、多租户隔离

集群:用来对同一服务的实例按物理机房 / 区域划分,实现就近调用、跨机房容灾。

配置示例:

复制代码
#分组名称用来对不同环境 / 业务线的服务做隔离,不同分组的服务默认无法互相发现
spring.cloud.nacos.discovery.group=em

# 配置当前实例所属的集群名称, 用来对同一服务的实例按物理机房 / 区域划分,实现就近调用、跨机房容灾
spring.cloud.nacos.discovery.cluster-name=ChenDu

这里我们再通过用不同端口运行多个服务的方式来模拟一下集群:

在运行配置中我们可以新建多个配置,勾选程序参数,配置运行端口就可以运行多个不同端口的相同服务:

可以看到实例数量发生改变。

添加多个集群:

3. 服务发现

想要获取到注册中心中已经注册的服务需要使用到@EnableDiscoveryCilent注解,写在需要服务发现的启动类上即可:

我们引入测试依赖在测试文件中进行测试:

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

spring和nacos都提供了用于获取服务的api

java 复制代码
package com.ting.order;


import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.exception.NacosException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.util.List;


@SpringBootTest
public class DiscoveryTest {
    //Spring提供,任何注册中心都可用
    @Autowired
    DiscoveryClient discoveryClient;

    //nacos提供,仅使用nacos时能用
    @Autowired
    NacosServiceDiscovery nacosServiceDiscovery;
    @Test
    void testDiscoveryClient(){

        for(String server: discoveryClient.getServices()) {
            //获取已有服务名
            System.out.println(server);
            //获取服务ip端口号
            List<ServiceInstance> instances = discoveryClient.getInstances(server);
            for (ServiceInstance instance : instances) {
                System.out.println(instance.getHost() + ":" + instance.getPort());
            }
        }
    }

    @Test
    void nacosServiceDiscovery() throws NacosException {

        for(String server: nacosServiceDiscovery.getServices()) {
            //获取已有服务名
            System.out.println(server);
            //获取服务ip端口号
            List<ServiceInstance> instances = nacosServiceDiscovery.getInstances(server);
            for (ServiceInstance instance : instances) {
                System.out.println(instance.getHost() + ":" + instance.getPort());
            }
        }
    }
}

可以看到输出内容是相同的:

这两个api了解即可,实际开发中远程调用有更好的方式。

4. 远程调用

接下来我们模拟一个订单模块调用商品模块的示例。

创建模块model用于存放实体:

创建实体:

java 复制代码
package com.ting.order.bean;

import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

@Data
public class Order {
    private Long id;
    private BigDecimal totalAmount;
    private Long userId;
    private String nickName;
    private List<Product> productList;
}
java 复制代码
package com.ting.product.bean;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class Product {
    private Long id;
    private BigDecimal price;
    private String productName;
    private int num;
}

在services中引入model依赖,否则service-order和service-product无法访问到:

XML 复制代码
        <dependency>
            <groupId>com.ting.study</groupId>
            <artifactId>model</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
java 复制代码
package com.ting.product.controller;

import com.ting.product.bean.Product;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@RestController
public class ProductController {
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Long productId) {
        Product product = new Product();
        product.setId(productId);
        product.setPrice(new BigDecimal("66"));
        product.setProductName("xiaomi" + productId);
        product.setNum(2);
        return product;
    }
}
java 复制代码
package com.ting.order.controller;
import java.math.BigDecimal;
import java.util.Arrays;

import com.ting.order.bean.Order;
import com.ting.product.bean.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@Slf4j
@RestController
public class OrderController {
    @Autowired
    DiscoveryClient discoveryClient;

    RestTemplate restTemplate = new RestTemplate();
    @GetMapping("/order")
    public Order createOrder( @RequestParam("userId") Long userId,
                              @RequestParam("productId") Long productId) {
        Product product = getProductFromRemote(productId);
        Order order = new Order();
        order.setId(productId);
        order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
        order.setUserId(userId);
        order.setNickName("Ting");
        order.setAddress("北京");
        order.setProductList(Arrays.asList(product));
        return order;
    }

    public Product getProductFromRemote(Long productId) {
        //获取第一个实例
        ServiceInstance serviceInstance = discoveryClient.getInstances("servers-product").get(0);
        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/product/" + productId;

        log.info("远程请求:{}", url);
        //远程发送请求
        return restTemplate.getForObject(url, Product.class);
    }
}

这里我们模拟在订单模块中获取商品信息:

可以看到成功获取了商品信息。我们代码里写的是默认访问第一个实例,那如果我们关掉9000端口的服务再次运行:

可以看到访问了9001,因为9000端口的实例挂掉以后就不会出现在nacos中。

相关推荐
逆境不可逃13 分钟前
Pi Agent 学习笔记:多个工具怎样并行执行
java
weixin_BYSJ198740 分钟前
【计算机毕设】基于SpringBoot与Vue的文物保护档案管理系统08621
vue.js·spring boot·spring cloud·微服务·架构·django·课程设计
Flynt1 小时前
Java 27 升级实测:默认值动得比新特性多,有个老参数会让 JVM 直接起不来
java·jvm·后端
Bs_MoneyMagnet1 小时前
基于springboot+vue的个人健康管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·vue3·springboot3·计算机毕业设计
星云API技术支持1 小时前
企业微信二次开发:群权限设置、成员管理与群资料维护的接口组合实践
java·前端·企业微信
niucloud-admin2 小时前
JAVA V6 多商户商城 开发文档——job 计划任务开发
java·python·github
海宇AI3 小时前
零信任架构实战:基于海宇柠檬查出险-登记证构建自动化车抵贷核保网关
java·人工智能·架构·自动化
Bs_MoneyMagnet4 小时前
基于springboot+vue的心理咨询预约与随访平台的设计与实现 源码+文档
vue.js·spring boot·后端·spring·毕业设计·旅游·计算机毕业设计
cfm_29144 小时前
单例模式详解
java
我叫张土豆4 小时前
本体论、DDD、SDD、TDD:AI 编程时代的四层认知栈
java·人工智能