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中。

相关推荐
木子墨5161 小时前
工程算法实战 | 从LRU到手写本地缓存:LinkedHashMap → 双向链表+哈希表 → Caffeine 原理
java·数据结构·算法·链表·缓存
无尽冬.1 小时前
个人八股之三层架构
java·经验分享·后端·架构·异世界
贫民窟的勇敢爷们1 小时前
SpringBoot多环境配置全解+配置优先级管控
java·spring boot·后端
霸道流氓气质2 小时前
Spring AI ChatMemory 对话记忆配置JDBC方式到Mysql数据库实战示例与原理讲解
数据库·人工智能·spring
tellmewhoisi2 小时前
单独抽取用户服务(请求不通):feign添加拦截器(添加token)
java·开发语言
Java面试题总结2 小时前
我删掉了项目里 80% 的 try-catch,系统反而更稳了
spring
YL200404262 小时前
035LRU缓存
java·leetcode·缓存
不像程序员的程序媛2 小时前
mysql 0000-00-00 00:00:00零日期问题
java·mysql
霸道流氓气质2 小时前
Spring @Scheduled 单线程陷阱:当设备重连阻塞了整个定时任务体系
java·spring boot·spring