Spring Cloud 核心组件——注册中心

1. 什么是微服务的注册中心

注册中心:服务管理,核心是有个服务注册表,心跳机制动态维护。

为什么要用?

微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题。

主流的注册中心:Zookeeper、Eureka、Consul、ETCD 等。

服务提供者 Provider:启动的时候向注册中心上报自己的网络信息。

服务消费者 Consumer:启动的时候向注册中心上报自己的网络信息,拉取 Provider 的相关网络信息。

2. 分布式应用知识CAP理论知识

CAP定理:指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition Tolerance(分区容错性),三者不可同时获得。

一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(所有节点在同一时间的数据完全一致,越多节点,数据同步越耗时)

可用性(A):负载过大后,集群整体是否还能响应客户端的读写请求。(服务一直可用,而且是正常响应时间)

分区容错性(P):分区容忍性,就是高可用性,一个节点崩了,并不影响其它的节点。(100个节点,挂了几个,不影响服务,越多机器越好)

CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡。

原因:

CA 满足的情况下,P 不能满足的原因:数据同步(C)需要时间,也要正常的时间内响应(A),那么机器数量就要少,所以P就不满足。

CP 满足的情况下,A 不能满足的原因:数据同步(C)需要时间,,机器数量也多(P),但是同步数据需要时间,所以不能再正常时间内响应,所以A就不满足。

AP 满足的情况下,C不能满足的原因:机器数量也多(P),正常的时间内响应(A),那么数据就不能及时同步到其他节点,所以C不满足。

注册中心选择:

Zookeeper:CP 设计,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的 leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足。

Eureka:AP 原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化。

结论:

分布式系统中P,肯定要满足,所以只能在 C 和 A 中二选一。没有最好的选择,最好的选择是根据业务场景来进行架构设计,如果要求一致性,则选择 Zookeeper,如金融行业;如果要去可用性,则 Eureka,如电商系统。

Eureka 原理图:

Spring Cloud 体系官方地址:http://projects.spring.io/spring-cloud/

参考:

https://www.jianshu.com/p/d32ae141f680
https://blog.csdn.net/zjcjava/article/details/78608892

3. 使用 IDEA 搭建 Eureka 服务中心 Server 端并启动

官方文档:http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#spring-cloud-eureka-server

第一步:创建项目

和创建普通的 Spring Boot 项目是一样的,只是需要选择下图所示依赖

第二步:添加注解 @EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

第三步:增加配置 application.yml(其实可以使用 application.properties,但官网上使用 yml,这里我们照抄官网)

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

第四步:访问注册中心页面

使用 http://localhost:8761/ 访问注册中心页面,这个地址按照配置文件中的来,这里注意下,按照配置文件说的,访问地址为:http://localhost:8761/eureka/

但不同版本,可能不一样,我所使用的版本是 Greenwich,它就不能添加 /eureka/ 否则会报 404 错误

Eureka 管理后台出现一串红色字体:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY‘RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

这个是警告,说明有服务上线率低。

关闭检查方法:Eureka 服务端配置文件加入

server:
  enable-self-preservation: false

注意:自我保护模式禁止关闭,默认是开启状态 true

4. 创建商品服务,并将服务注册到注册中心

第一步:创建一个 Spring Boot 应用,增加服务注册和发现依赖


第二步:模拟商品信息,存储在内存中

第三步:开发商品列表接口,商品详情接口

第四步:配置文件加入注册中心地址

server:
  port: 8771

#指定注册中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

#服务的名称
spring:
  application:
    name: product-service

我们可以用当前项目启动多个实例,参考教程:https://blog.csdn.net/zhou520yue520/article/details/81167841

启动后,我们可以登录下访问注册中心页面,看到如下效果:

为什么只加一个注册中心地址,就可以注册?

官网解释:By having spring-cloud-starter-netflix-eureka-client on the classpath, your application automatically registers with the Eureka Server.(也就是说,这样 Jar 包在类路径上,就可以自动识别)

5.常用的服务间的调用方式

RPC:远程过程调用,像调用本地服务(方法)一样调用服务器的服务。支持同步、异步调用。客户端和服务器之间建立 TCP 连接,可以一次建立一个,也可以多个调用复用一次链接。PRC 数据包小。

Rest:Http 请求,支持多种协议和功能。开发方便成本低。Http 数据包大。类似 HttpClient,URLConnection。

6.订单服务调用商品服务获取商品信息

第一步:创建 order_service 项目

注意:调用方需要引入 Ribbon 依赖

第二步:使用 Ribbon(类似 HTTPClient,URLConnection)

启动类增加注解

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

第三步:开发伪下单接口

第四步:根据名称进行调用商品,获取商品详情

注意:红字标识的就是上面商品服务的 spring.application.name

@Service
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public ProductOrder save(int userId, int productId) {

        Object obj = restTemplate.getForObject("http://product-service/api/v1/product/find?id="+productId, Object.class);

        System.out.println(obj);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        return productOrder;
    }
}

当我们用请求多次访问时,可以从控制台看到端口号的不同,说明这是从不同端口的应用返回的数据

商品服务的 Controller 如下:

@RestController
@RequestMapping("/api/v1/product")
public class ProductController {

    @Value("${server.port}")
    private String port;

    @Autowired
    private ProductService productService;

    /**
     * 获取所有商品列表
     * @return
     */
    @RequestMapping("list")
    public Object list(){
        return productService.listProduct();
    }

    /**
     * 根据id查找商品详情
     * @param id
     * @return
     */
    @RequestMapping("find")
    public Object findById(int id){

        Product product = productService.findById(id);

        Product result = new Product();
        BeanUtils.copyProperties(product,result);
        result.setName( result.getName() + " data from port="+port );
        return result;
    }
}

我们还可以通过另一种调用方式进行调用

//调用方式二
ServiceInstance instance = loadBalancer.choose("product-service");
String url = String.format("http://%s:%s/api/v1/product/find?id="+productId, instance.getHost(),instance.getPort());
RestTemplate restTemplate = new RestTemplate();
Object obj = restTemplate.getForObject(url, Object.class);
//Map<String,Object> productMap = restTemplate.getForObject(url, Map.class);

调用原理:

1)首先从注册中心获取 Provider 的列表

2)通过一定的策略选择其中一个节点

3)再返回给 restTemplate 调用

我们也可以自定义负载均衡策略,官网说明:http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html#_customizing_the_ribbon_client_by_setting_properties

server:
  port: 8781

#指定注册中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

#服务的名称
spring:
  application:
    name: order-service

#自定义负载均衡策略
product-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

策略选择:

1)如果每个机器配置一样,则建议不修改策略 (推荐)

2)如果部分机器配置强,则可以改为 WeightedResponseTimeRule

7.使用 Feign 改造订单服务 

Feign: 伪 RPC 客户端(本质还是用http)

官方文档: https://cloud.spring.io/spring-cloud-openfeign/

第一步:加入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第二步:启动类增加 @EnableFeignClients

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

第三步:增加一个接口并使用注解 @FeignClient(name="product-service")

package com.jwen.order_service.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 商品服务客户端
 */
@FeignClient(name = "product-service")
public interface ProductClient {
    @GetMapping("/api/v1/product/find")
    String findById(@RequestParam(value = "id") int id);
}

注意点:

1)服务名和 Http 方法必须对应

2)使用 RequestBody,应该使用 @PostMapping

3)多个参数的时候,通过 @RequestParam(value = "id") int id 方式调用

第四步:更改调用方式编码

package com.jwen.order_service.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.jwen.order_service.domain.ProductOrder;
import com.jwen.order_service.service.ProductClient;
import com.jwen.order_service.service.ProductOrderService;
import com.jwen.order_service.utils.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;

@Service
public class ProductOrderServiceImpl implements ProductOrderService {
    @Autowired
    private ProductClient productClient;

    @Override
    public ProductOrder save(int userId, int productId) {
        String response = productClient.findById(productId);
        JsonNode jsonNode = JsonUtils.str2JsonNode(response);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());
        productOrder.setProductName(jsonNode.get("name").toString());
        productOrder.setPrice(Integer.parseInt(jsonNode.get("price").toString()));
        return productOrder;
    }
}
JsonUtils 工具类的代码
package com.jwen.order_service.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

/**
 * json工具类
 */
public class JsonUtils {
    private static final ObjectMapper objectMappper = new ObjectMapper();
    /**
     * json字符串转JsonNode对象的方法
     */
    public static JsonNode str2JsonNode(String str){
        try {
            return  objectMappper.readTree(str);
        } catch (IOException e) {
            return null;
        }
    }
}

Ribbon 和 Feign 两个之间,应该选择 Feign。Feign 默认集成了 Ribbon。写起来更加思路清晰和方便。采用注解方式进行配置,配置熔断等方式方便

超时配置

#修改调用超时时间
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000

模拟接口响应慢,线程睡眠新的方式

try {
    TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
    e.printStackTrace();
}

原文地址:https://www.cnblogs.com/jwen1994/p/11408511.html

时间: 2024-08-15 01:07:33

Spring Cloud 核心组件——注册中心的相关文章

Spring Cloud之注册中心搭建

一.注册中心服务端搭建 1)引入相关Maven坐标 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> 2)加入相关注解 @EnableEurekaServer 3)相关配置属性,application.yml

spring cloud Eureka注册中心集群搭建

1.创建springcloud-eureka maven项目 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/m

Spring Cloud - Nacos注册中心入门单机模式及集群模式

近几年微服务很火,Spring Cloud提供了为服务领域的一整套解决方案.其中Spring Cloud Alibaba是我们SpringCloud的一个子项目,是提供微服务开发的一站式解决方案. 包含微服务开发的必要组件,基于SpringCloud 符合SpringCloud标准,是阿里的微服务的解决方案. 文档:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md Nacos的安装与启动 Nacos是

spring cloud(服务注册中心及服务提供者——初学一)

一.创建服务注册中心 1.创建spring boot项目 2.引入eureka-server依赖(也可以在创建项目时勾选eureka-server服务,自动生成依赖) 注意版本号,版本不对可能启动会报错,本次使用Dalston.SR1版本 3.启动类中开启服务注册中心功能 4.在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.properties中问增加如下配置: 5.浏览器访问  localhost:1111

spring cloud 搭建注册中心Eureka(集群模式)

集群 注册中心这么关键的服务,如果是单点话,遇到故障就是毁灭性的.在一个分布式系统中,服务注册中心是最重要的基础部分,理应随时处于可以提供服务的状态.为了维持其可用性,使用集群是很好的解决方案.Eureka通过互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署. 新建3个配置文件 application-peer1.yml spring: application: name: Service #应用名称,也是服务注册的

Spring Cloud Eureka 注册中心高可用机制

一.Eureka 正常工作流程 Service 服务作为 Eureka Client 客户端需要在启动的时候就要向 Eureka Server 注册中心进行注册,并获取最新的服务列表数据. Eureka Server 之间通过 Peer To Peer 模式复制最新数据. Eureka Client 通过心跳机制定时向 Eureka Server 续约,上报自己的状态,并获取最新的服务列表数据. Eureka Client 在本地有一个localRegionApps变量,用来保存从 Eureka

Spring Cloud Gateway注册到服务器中心(Consul)

Spring Cloud Gateway注册到服务器中心(Consul) 准备环境 启动Consul(./consul agent -dev)作为服务中心,默认是8500端口,然后启动spring-cloud-provider(9001端口)和spring-cloud-provider-second(9002端口)两个项目作为微服务. 在Consul管理后台可以看见两个服务启动: 添加Spring Cloud Gateway项目的依赖项 POM内增加如下依赖: <dependency> <

Spring Cloud学习--配置中心(Config)

Spring Cloud学习--配置中心(Config) 一 Spring Cloud Config简介 二 编写 Config Server 三 编写Config Client 四 使用refresh端点手动刷新配置 五 Spring Config Server与Eurelka配合使用 六 Config Server的高可用 一. Spring Cloud Config简介 微服务要实现集中管理微服务配置.不同环境不同配置.运行期间也可动态调整.配置修改后可以自动更新的需求,Spring Cl

spring cloud服务注册与发现

1.注册中心服务端默认90秒检测一次,看服务是否还存活,不存活则删除掉服务,还存活则继续注册上去 2. spring: profiles: dev cloud: config: name: cleanup-center profile: dev,discoveryClient discovery: enabled: true service-id: akucun-framework-config-server eureka: client: service-url: defaultZone: h