代码地址

2. nacos 服务注册与发现

在大型项目中,巨型单体式应用肯定是不现实的,需要将应用拆分为小的,互相连接的微服务。一个微服务完成某个特定的任务,且每个微服务都是独立的个体。

2.1 什么是服务注册与发现

  • 服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
  • 服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实例的访问。

简单来说就是把一个一个微服务注册到一个注册中心去,这个注册中心管理着所有的微服务,服务之间靠注册中心这个纽带维持相互的关系。服务之间互动调用也是从注册中心发现另外一个微服务完成调用。

image.png image.png

2.2 使用nacos做服务注册与发现中心

使用手册 Nacos-discovery

2.2.1版本选择

版本查询
alibaba版本推荐

  • spring-boot 2.4.2
  • spring-cloud 2020.0.3
  • spring-cloud-alibaba 2021.1

    2.2.2 项目搭建

    服务组件

    • Lombok 1.18.20
    • mybatisPlus 3.3.1
    • maven 3.6.3

微服务组成

  • cloud-common 公共内容
  • consumer-feign-order8002 服务消费者 基于feign的服务调用
  • consumer-order8001 服务消费者 没有feign基于restTemplate调用
  • provider-product7001 服务提供者
  • provider-product7002 服务提供者

两个微服务提供者代码相同,基于同一个配置文件开启服务
bootstrap.yml

  1. spring:
  2. application:
  3. name: provider-product
  4. cloud:
  5. nacos:
  6. # 配置
  7. config:
  8. namespace: 3d0a77b8-817f-499b-bfda-f90d5a6e4dab
  9. server-addr: 192.168.19.128:13306
  10. group: DEFAULT_GROUP
  11. file-extension: yaml
  12. username: nacos
  13. password: nacos
  14. # 注册
  15. discovery:
  16. namespace: 3d0a77b8-817f-499b-bfda-f90d5a6e4dab
  17. server-addr: 192.168.19.128:13306
  18. group: DEFAULT_GROUP
  19. username: nacos
  20. password: nacos

provider-product.yaml
image.png

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://192.168.19.128/cloudalibaba?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
  5. username: root
  6. password: 123456
  7. mybatis-plus:
  8. configuration:
  9. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  10. #配置无数据时 返回null
  11. call-setters-on-nulls: true
  12. # 配置全局主键生成策略
  13. global-config:
  14. db-config:
  15. table-prefix: t_
  16. id-type: auto

两个服务提供者的配置文件和 provider-product.yaml 一样 仅提供连接数据库配置

当启动所以服务后 都会注册到nacos,并且 provider-product是多实例
image.png

2.2.3 服务调用

provider-product提供一个查询商品的接口

  1. @GetMapping("/product/getProduct/{id}")
  2. public CommonResult<Product> getProduct(@PathVariable("id") Integer id) {
  3. Optional<Product> optional = Optional.ofNullable(productService.findBuyId(id));
  4. return optional.map(CommonResult::ok).orElseGet(CommonResult::error);
  5. }

2.2.3.1 consumer-order8001调用provider-product

使用restTemplate对比4种调用方式 由繁入简

  1. 基于http直接通过ip调用 只能单独访问某个服务,不能实现负载


注入bean

  1. @Bean("restTemplate")
  2. public RestTemplate getRestTemplate() {
  3. return new RestTemplate();
  4. }
  1. @GetMapping("/ip/createOrder/{pid}")
  2. public CommonResult createOrder(@PathVariable("pid") Integer pid) {
  3. CommonResult<Product> result = restTemplate.getForObject("http://127.0.0.1:7001/product/getProduct/" + pid, CommonResult.class);
  4. JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(result));
  5. if (jsonObject.getInteger("code").equals(0)) {
  6. Product product = jsonObject.getObject("data", Product.class);
  7. orderService.createOrder(product);
  8. return CommonResult.ok();
  9. }
  10. return CommonResult.error();
  11. }


  1. 通过discoveryClient获取服务提供者,再通过服务提供者的ip端口访问 同样不能负载均衡

    1. @Autowired
    2. private DiscoveryClient discoveryClient;
    3. /**
    4. * 获取生产者实例再使用restTemplate调用微服务
    5. *
    6. * @param pid
    7. */
    8. @GetMapping("/service/createOrder/{pid}")
    9. public CommonResult createOrder(@PathVariable("pid") Integer pid) {
    10. String serviceName = "provider-product";
    11. //从nacos中获取服务地址
    12. ServiceInstance serviceInstance = discoveryClient.getInstances(serviceName).get(0);
    13. String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
    14. //通过restTemplate调用商品微服务
    15. CommonResult<Product> result = restTemplate.getForObject("http://" + url + "/product/getProduct/" + pid, CommonResult.class);
    16. JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(result));
    17. if (jsonObject.getInteger("code").equals(0)) {
    18. Product product = jsonObject.getObject("data", Product.class);
    19. orderService.createOrder(product);
    20. return CommonResult.ok();
    21. }
    22. return CommonResult.error();
    23. }
  2. 自定义随机负载

    1. @GetMapping("/myBalance/createOrder/{pid}")
    2. public CommonResult createOrder(@PathVariable("pid") Integer pid) {
    3. String serviceName = "provider-product";
    4. //从nacos中获取服务地址
    5. List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceName);
    6. //根据随机值调用服务
    7. int index = RandomUtil.randomInt(instanceList.size());
    8. ServiceInstance serviceInstance = instanceList.get(index);
    9. String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
    10. //通过restTemplate调用商品微服务
    11. CommonResult<Product> result = restTemplate.getForObject("http://" + url + "/product/getProduct/" + pid, CommonResult.class);
    12. JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(result));
    13. if (jsonObject.getInteger("code").equals(0)) {
    14. Product product = jsonObject.getObject("data", Product.class);
    15. orderService.createOrder(product);
    16. return CommonResult.ok();
    17. }
    18. return CommonResult.error();
    19. }


  3. 使用restTemplate实现负载均衡

    早在Spring Cloud Hoxton.M2中就已经放弃了 Netflix Ribbon 的使用,取而代之的是自研的localbalancer进行负载均衡
    Spring Cloud Load Balancer使用指南
    加入pom

    1. <dependency>
    2. <groupId>org.springframework.cloud</groupId>
    3. <artifactId>spring-cloud-loadbalancer</artifactId>
    4. </dependency>

    添加@LoadBalanced 实现负载均衡

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


    ```java @GetMapping(“/rabbinBalance/createOrder/{pid}”) public CommonResult createOrder(@PathVariable(“pid”) Integer pid) {

    1. String serviceName = "provider-product";
  1. String url = "http://" + serviceName + "/product/getProduct/" + pid;
  2. //通过restTemplate调用商品微服务
  3. CommonResult<Product> result = restTemplateBalance.getForObject(url, CommonResult.class);
  4. JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(result));
  5. if (jsonObject.getInteger("code").equals(0)) {
  6. Product product = jsonObject.getObject("data", Product.class);
  7. orderService.createOrder(product);
  8. return CommonResult.ok();
  9. }
  10. return CommonResult.error();
  11. }
  1. <a name="TtwGm"></a>
  2. #### <br />
  3. <a name="3piXv"></a>
  4. #### 2.2.3.2 consumer-order8002使用openFeign负载均衡
  5. pom加入负载均衡和openFeign的集成<br />启动类添加注解 表示启动openFeign @EnableFeignClients
  6. ```xml
  7. <dependency>
  8. <groupId>org.springframework.cloud</groupId>
  9. <artifactId>spring-cloud-starter-openfeign</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.cloud</groupId>
  13. <artifactId>spring-cloud-loadbalancer</artifactId>
  14. </dependency>

添加openFeign 调用Service 直接使用SpringMVC的调用方法

  1. @Service
  2. @FeignClient("provider-product")
  3. public interface ProductService {
  4. /**
  5. * //@FeignClient+@GetMapping 就是一个完整的请求路径 http://provider-product/product/getProduct/{id};
  6. * 指定调用的方法
  7. *
  8. * @param id
  9. * @return
  10. */
  11. @GetMapping("/product/getProduct/{id}")
  12. CommonResult<Product> getProduct(@PathVariable("id") Integer id);
  13. }

实现负载均衡效果

  1. @GetMapping("/createOrder/{pid}")
  2. public CommonResult createOrder(@PathVariable("pid") Integer pid) {
  3. CommonResult<Product> productCommonResult = productService.getProduct(pid);
  4. log.info("通过feign调用的数据:" + productCommonResult);
  5. if (productCommonResult.getCode().equals(0)) {
  6. orderService.createOrder(productCommonResult.getData());
  7. return CommonResult.ok();
  8. }
  9. return CommonResult.error();
  10. }

修改控制台效果
由于使用loadbalancer而不使用ribbon 所以修改权重是没有效果的!下线依然有效

image.png