• Nacos Discovery--服务治理


    目录

    1.服务治理介绍

            1.1 nacos简介

            1.2 nacos实战入门

                    1.2.1 搭建nacos环境

                    1.2.2 将商品微服务注册到nacos

                    1.2.3 将订单微服务注册到nacos

                    1.2.4 多服务启动控制台配置

    2. 实现服务调用的负载均衡

            2.1 什么是负载均衡

            2.2 DiscoveryClient实现负载均衡

            2.3 Ribbon 实现负载均衡

    3. 基于Feign实现服务调用

            3.1 什么是Feign

            3.2 Feign 的使用

    4.Feign参数传递


    1.服务治理介绍

    先来思考一个问题 通过上一章的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 (ip,端口)等硬编码到了代码中,这种做法存在许多问题:

    • 一旦服务提供者地址变化,就需要手工修改代码

    • 一旦是多个服务提供者,无法实现负载均衡功能

    • 一旦服务变得越来越多,人工维护调用关系困难

    那么应该怎么解决呢, 这时候就需要通过注册中心动态的实现服务治理。 什么是服务治理 服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现

    • 服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服 务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中 的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。

    • 服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实 例的访问。

     

     

    通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要 的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:

    1. 服务发现:

      • 服务注册:保存服务提供者和服务调用者的信息

      • 服务订阅(发现):服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息

    2. 服务配置:

      • 配置订阅:服务提供者和服务调用者订阅微服务相关的配置

      • 配置下发:主动将配置推送给服务提供者和服务调用者

    3. 服务健康检测

      • 检测服务提供者的健康情况,如果发现异常,执行服务剔除

    常见的注册中心

    • Zookeeper zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式 应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用 配置项的管理等。

    • Eureka Eureka是Springcloud Netflix中的重要组件,主要作用就是做服务注册和发现。但是现在已经闭 源

    • Consul Consul是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现 和配置管理的功能。Consul的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value 存储、多数据中心和分布式一致性保证等特性。Consul本身只是一个二进制的可执行文件,所以 安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。

    • Nacos Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 Spring Cloud Alibaba 组件之一,负责服务注册发现和服务配置,可以这样认为nacos=eureka+config。

            1.1 nacos简介

    Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速 实现动态服务发现、服务配置、服务元数据及流量管理。 从上面的介绍就可以看出,nacos的作用就是一个注册中心,用来管理注册上来的各个微服务。

            1.2 nacos实战入门

                    1.2.1 搭建nacos环境

    第1步: 安装nacos

    下载地址: https://github.com/alibaba/nacos/releases
    下载zip格式的安装包,然后进行解压缩操作 

      

      第2步: 启动nacos

    #切换目录
    cd nacos/bin
    #命令启动
    startup.cmd -m standalone

     点击输入cmd 回车即可

     

    输入命令 startup.cmd -m standalone 

    出现图中内容就成功了

     

     第3步: 访问nacos 打开浏览器输入http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos

     

                    1.2.2 将商品微服务注册到nacos

    参照之前的博客内容:

    接下来开始修改shop-product 模块的代码, 将其注册到nacos服务上

    1 在shop-common模块的pom.xml中添加nacos的依赖

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    4. dependency>

    注意在父模块中是否导入了alibaba  (这里的父模块指的是springboot-shop

    1. <dependencyManagement>
    2. <dependencies>
    3. <dependency>
    4. <groupId>org.springframework.cloudgroupId>
    5. <artifactId>spring-cloud-dependenciesartifactId>
    6. <version>${spring-cloud.version}version>
    7. <type>pomtype>
    8. <scope>importscope>
    9. dependency>
    10. <dependency>
    11. <groupId>com.alibaba.cloudgroupId>
    12. <artifactId>spring-cloud-alibaba-dependenciesartifactId>
    13. <version>${spring-cloud-alibaba.version}version>
    14. <type>pomtype>
    15. <scope>importscope>
    16. dependency>
    17. dependencies>
    18. dependencyManagement>

     

      2 在主类上添加@EnableDiscoveryClient注解

    @EnableDiscoveryClient

     

      3 在application.yml中添加nacos服务的地址

    1. spring:
    2. cloud:
    3. nacos:
    4. discovery:
    5. server-addr: localhost:8848

     

    4 启动服务, 观察nacos的控制面板中是否有注册上来的商品微服务

     

      

                    1.2.3 将订单微服务注册到nacos

    接下来开始修改shop_order 模块的代码, 将其注册到nacos服务上

    1 在pom.xml中添加nacos的依赖

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    4. dependency>

    2 在主类上添加@EnableDiscoveryClient注解  

    3 在application.yml中添加nacos服务的地址

    4 修改OrderController, 实现微服务调用

    1. package com.ruojuan.shoporder.controller;
    2. import com.ruojuan.model.Order;
    3. import com.ruojuan.model.Product;
    4. import com.ruojuan.model.User;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.cloud.client.ServiceInstance;
    7. import org.springframework.cloud.client.discovery.DiscoveryClient;
    8. import org.springframework.web.bind.annotation.PathVariable;
    9. import org.springframework.web.bind.annotation.RequestMapping;
    10. import org.springframework.web.bind.annotation.RestController;
    11. import org.springframework.web.client.RestTemplate;
    12. import java.util.List;
    13. import java.util.Random;
    14. /**
    15. * @author ruojuan
    16. * @site www.ruojuan.com
    17. * @company 玉渊工作室
    18. * @create 2022年11月28日 21:21
    19. **/
    20. @RestController
    21. @RequestMapping("/order1")
    22. public class OrderConroller_DiscoveryClient {
    23. @Autowired
    24. private RestTemplate restTemplate;
    25. @Autowired
    26. private DiscoveryClient discoveryClient;
    27. @RequestMapping("/get/{uid}/{pid}")
    28. public Order get(@PathVariable("uid") Integer uid,
    29. @PathVariable("pid") Integer pid){
    30. //我们可以通过服务名,拿到各个节点的信息
    31. List instances = discoveryClient.getInstances("shop-product");
    32. //随机产生0或者1的选择
    33. int index = new Random().nextInt(instances.size());
    34. ServiceInstance serviceInstance = instances.get(index);
    35. String url = serviceInstance.getHost()+":"+serviceInstance.getPort();
    36. /*
    37. * 要在订单微服务调用 用户微服务,商品微服务,也就意味着跨项目调用
    38. * */
    39. User user = restTemplate.getForObject("http://localhost:8070/user/get/" + uid, User.class);
    40. Product product = restTemplate.getForObject("http://"+url+"/product/get/" + pid, Product.class);
    41. Order order = new Order();
    42. order.setUsername(user.getUsername());
    43. order.setUid(user.getUid());
    44. order.setPprice(product.getPprice());
    45. order.setPname(product.getPname());
    46. order.setPid(product.getPid());
    47. order.setOid(System.currentTimeMillis());
    48. order.setNumber(product.getStock());
    49. return order;
    50. }
    51. }

    DiscoveryClient是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服 务

    5 启动服务

    观察nacos的控制面板中是否有注册上来的订单微服务,然后通过访问消费者服务验证调用是否成功

                    1.2.4 多服务启动控制台配置

    2. 实现服务调用的负载均衡

            2.1 什么是负载均衡

    通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上 进行执行。 根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。 服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡 而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请 求。

    我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供 者执行。

            2.2 DiscoveryClient实现负载均衡

    1 通过idea再启动一个shop-product 微服务,设置其端口为8081

      2 通过nacos查看微服务的启动情况

    1. @RestController
    2. @Slf4j
    3. @RequestMapping("/order")
    4. public class OrderController {
    5. @Autowired
    6. private RestTemplate restTemplate;
    7. @Autowired
    8. private DiscoveryClient discoveryClient;
    9. @Autowired
    10. private OrderService orderService;
    11. //准备买1件商品
    12. @GetMapping("/prod/{pid}")
    13. public Order order(@PathVariable("pid") Integer pid) {
    14. log.info(">>客户下单,这时候要调用商品微服务查询商品信息");
    15. //从nacos中获取服务地址
    16. //自定义规则实现随机挑选服务
    17. List instances = discoveryClient.getInstances("shop-product");
    18. int index = new Random().nextInt(instances.size());
    19. ServiceInstance serviceInstance = instances.get(index);
    20. String url = serviceInstance.getHost() + ":" +
    21. serviceInstance.getPort();
    22. log.info(">>从nacos中获取到的微服务地址为:" + url);
    23. //通过restTemplate调用商品微服务
    24. Product product = restTemplate.getForObject("http://" + url +
    25. "/product/detail/" + pid, Product.class);
    26. Order order = new Order();
    27. order.setUid(1);
    28. order.setUsername("测试用户");
    29. order.setPid(product.getPid());
    30. order.setPname(product.getPname());
    31. order.setPprice(product.getPprice());
    32. order.setNumber(1);
    33. orderService.createOrder(order);
    34. return order;
    35. }
    36. }

     

            2.3 Ribbon 实现负载均衡

    Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡

    第1步:在RestTemplate 的生成方法上添加@LoadBalanced注解

    1. @Bean
    2. @LoadBalanced
    3. public RestTemplate restTemplate() {
    4. return new RestTemplate();
    5. }

    第2步:修改服务调用的方法

     

    1. package com.ruojuan.shoporder.controller;
    2. import com.ruojuan.model.Order;
    3. import com.ruojuan.model.Product;
    4. import com.ruojuan.model.User;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.PathVariable;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. import org.springframework.web.client.RestTemplate;
    10. /**
    11. * @author ruojuan
    12. * @site www.ruojuan.com
    13. * @company 玉渊工作室
    14. * @create 2022年11月24日 15:23
    15. **/
    16. //@RestController
    17. //@RequestMapping("/order")
    18. public class OrderController_Ribbon {
    19. @Autowired
    20. private RestTemplate restTemplate;
    21. // @Autowired
    22. // private DiscoveryClient discoveryClient;
    23. @RequestMapping("/get/{uid}/{pid}")
    24. public Order get(@PathVariable("uid") Integer uid,
    25. @PathVariable("pid") Integer pid){
    26. // //我们可以通过服务名,拿到各个节点的信息
    27. // List instances = discoveryClient.getInstances("shop-product");
    28. // //随机产生0或者1的选择
    29. // int index = new Random().nextInt(instances.size());
    30. // ServiceInstance serviceInstance = instances.get(index);
    31. // String url = serviceInstance.getHost()+":"+serviceInstance.getPort();
    32. /*
    33. * 要在订单微服务调用 用户微服务,商品微服务,也就意味着跨项目调用
    34. * */
    35. /*
    36. * http://shop-user/user/get/方式访问第三方服务
    37. * 那么http://localhost:8080/user/get/方式就用不了了
    38. * */
    39. User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
    40. Product product = restTemplate.getForObject("http://shop-product/product/get/" + pid, Product.class);
    41. Order order = new Order();
    42. order.setUsername(user.getUsername());
    43. order.setUid(user.getUid());
    44. order.setPprice(product.getPprice());
    45. order.setPname(product.getPname());
    46. order.setPid(product.getPid());
    47. order.setOid(System.currentTimeMillis());
    48. order.setNumber(product.getStock());
    49. return order;
    50. }
    51. }

    Ribbon支持的负载均衡策略 Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接口为 com.netflix.loadbalancer.IRule , 具体的负载策略如下图所示:

     

    我们可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下

    service-product: # 调用的提供者的名称
        ribbon:
            NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 

    3. 基于Feign实现服务调用

            3.1 什么是Feign

    Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务 一样简单, 只需要创建一个接口并添加一个注解即可。 Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负 载均衡的效果。

            3.2 Feign 的使用

    1 加入Fegin的依赖

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

    2 在主类上添加Fegin的注解

    1. @SpringBootApplication
    2. @EnableDiscoveryClient
    3. @EnableFeignClients//开启Fegin
    4. public class OrderApplication {}

      3 创建一个service, 并使用Fegin实现微服务调用

    1. package com.zking.service;
    2. @FeignClient("service-product")//声明调用的提供者的name
    3. public interface ProductService {
    4. //指定调用提供者的哪个方法
    5. //@FeignClient+@GetMapping 就是一个完整的请求路径
    6. //http://serviceproduct/product/{pid}
    7. @GetMapping(value = "/product/{pid}")
    8. Product findByPid(@PathVariable("pid") Integer pid);
    9. }
    1. package com.ruojuan.shoporder.controller;
    2. import com.ruojuan.model.Order;
    3. import com.ruojuan.model.Product;
    4. import com.ruojuan.model.User;
    5. import com.ruojuan.shoporder.service.ProductService;
    6. import org.checkerframework.checker.units.qual.A;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.cloud.client.ServiceInstance;
    9. import org.springframework.cloud.client.discovery.DiscoveryClient;
    10. import org.springframework.web.bind.annotation.PathVariable;
    11. import org.springframework.web.bind.annotation.RequestMapping;
    12. import org.springframework.web.bind.annotation.RestController;
    13. import org.springframework.web.client.RestTemplate;
    14. import javax.servlet.http.HttpServletRequest;
    15. import java.util.List;
    16. import java.util.Random;
    17. /**
    18. * @author ruojuan
    19. * @site www.ruojuan.com
    20. * @company 玉渊工作室
    21. * @create 2022年11月24日 15:23
    22. **/
    23. @RestController
    24. @RequestMapping("/order")
    25. public class OrderController {
    26. @Autowired
    27. private ProductService productService;
    28. @Autowired
    29. private RestTemplate restTemplate;
    30. @RequestMapping("/get/{uid}/{pid}")
    31. public Order get(@PathVariable("uid") Integer uid,
    32. @PathVariable("pid") Integer pid,
    33. HttpServletRequest request){
    34. User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
    35. Product product = productService.get(pid);
    36. Order order = new Order();
    37. order.setUsername(user.getUsername());
    38. order.setUid(user.getUid());
    39. order.setPprice(product.getPprice());
    40. order.setPname(product.getPname());
    41. order.setPid(product.getPid());
    42. order.setOid(System.currentTimeMillis());
    43. order.setNumber(product.getStock());
    44. return order;
    45. }
    46. }

     

    4.Feign参数传递

    1. @Slf4j
    2. @RestController
    3. @RequestMapping("/feign")
    4. public class FeignDemoController {
    5. @RequestMapping("/findByParameter")
    6. public String findByParameter(String name,Double price){
    7. log.info("服务提供者日志:{}",name);
    8. return "hello:"+name;
    9. }
    10. @RequestMapping("/findByParameter2")
    11. public String findByParameter2(
    12. @RequestParam("name") String name,
    13. @RequestParam("price") Double price){
    14. log.info("服务提供者日志:{},{}",name,price);
    15. return "hello:"+name+price;
    16. }
    17. @RequestMapping("/findByPathVariable")
    18. public String findByPathVariable(@PathVariable String name){
    19. log.info("服务提供者日志:{}",name);
    20. return "hello:"+name;
    21. }
    22. @RequestMapping("/findByRequestBody")
    23. public Product findByRequestBody(@RequestBody Product product){
    24. log.info("服务提供者日志:{}",product.getPname());
    25. return product;
    26. }
    27. }

  • 相关阅读:
    关于ThreadPoolTaskExecutor线程池的配置
    YYDS!由浅入深学习阿里JDK源码,已在阿里内部疯拿3个金奖
    【资损】业务产品分析资损防控规范
    Excel 函数大全应用,包含各类常用函数
    经过腾讯云这波故障,我想表扬的点和学到的职场保命法则。
    DOM基础应用
    26.在springboot中使用thymeleaf判断语句(if,switch)
    【渗透测试】|基于dvwa的CSRF初级,中级,高级
    获得微店商品详情 API
    设计循环队列(leetcode 622)
  • 原文地址:https://blog.csdn.net/weixin_67338832/article/details/128090001