Spring Cloud 入门(一)简介、服务治理


代码在 https://github.com/betterGa/SpringCloudDemo

一、简介


1、集群 和 分布式

(1)集群
     一台服务器无法负荷高并发的数据访问,需要设置更多的服务器一起分担压力。(一台不行就设置十台一起分担压力,十台不行就一百台 🤗 。)从 物理层面 解决高并发的问题,就像 春运期间火车站 多开购票窗口,可以理解成 很多人干同一件事,来分摊压力。
    
(2)分布式
     将一个复杂问题 拆分成 若干个 简单的小问题, 将一个大型的项目架构拆分为若干个微服务来协同完成。从 软件设计层面 解决问题, 比如 将购票分为 统计出发地与目的地、查询是否有票、统一购买票 等步骤,分别由不同的人来完成这些小的工作,最后将所有的工作结果进行整合,实现更大的需求。


2、微服务
  • 什么是微服务
         微服务是一种架构风格,是一种架构设计方式,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
        
  • 为什么需要微服务
         传统开发模式下,绝大多数的 web 应用都是单体架构的风格来进行构建,这就使得所有的接口,业务逻辑层,数据持久层全部都被打包在一个 web 应用中,并且布置在一台服务器上,使得不同的模块之前也高耦合在一起,这种开发模式使得多团队协作开发的开发成本极高。
        
    在这里插入图片描述
         可以看到,每个团队都有自己的代码,只要维护好自己的项目,然后通过某种统一的协议,各个服务之间可以进行通信,提供对外接口 ,其他团队调用就行;项目部署起来,成为微服务,再通过微服务管理整合起来,就是整个微服务架构。这样让运维、开发、部署的工作都简化了。
        
  • 单体应用存在的问题
    (1)随着业务的发展,开发变得越来越复杂。
    (2)单一功能进行修改时,需要对整个系统进行打包部署。
    (3)一个模块出现问题,很可能导致整个系统崩溃。
    (4)多个团队同时对数据进行操作管理,容易产生安全漏洞。
    (5)各模块都使用相对统一的技术进行开发,各个模块 很难根据实际情况 选择更加合适的技术框架,局限性很大,系统的延展性比较低。
    (6)模块内容过于复杂,新人上手比较费时。
         其实主要就是耦合度高的问题 😂。微服务、 分布式其实是在架构结构解耦合。 解耦合这个思路在整个编程中都很重要。
        
  • 微服务的优点
    (1)各个服务的开发、测试、部署都是相互 独立 的。可以 针对某一个特定的服务 进行更多的操作,比如 负载均衡 等。
    (2)当有一个新的需求加入时,传统项目需要结合各方面考虑影响,兼容性、影响都 等,微服务就不存在这样的问题,省事省力又省心,是可以自治的。
    (3)使用微服务将项目拆分后,只需要保证对外接口的正常运行,大大降低了各个模块之间的耦合性,极大的提高开发效率。
        
  • 微服务的弊端
    (1)微服务的拆分基于业务,不能随心所欲的拆分,所以如何拆分,对于项目架构来说是非常重要且极具挑战的任务。
    (2)涉及到服务之间的调用时,常常需要和另外一个服务的提供方进行沟通,若是两个完全不同的公司或者部门,沟通成本比较大;某服务的对外接口要进行修改,也需要与其他服务调用方进行沟通。
    (3)由于各个服务相互独立,数据也是独立,当多个服务的接口进行操作时,如何保证数据的一致性是一个难点。数据统一性是微服务里面的一个难题
        
3、为什么选择 Spring Cloud

     虽然微服务也有很多缺点,但是瑕不掩瑜,总体来讲,微服务还是实现分布式架构的一个非常好的方式,是当下非常热点的技术,也是未来技术发展的趋势。当下较为常见的微服务框架是 Spring Cloud 和 dubbo。那我们为什么选择 Spring Cloud 呢,原因如下:
(1)Spring Cloud 是完全基于 Spring Boot,服务调用是基于 REST API ,整合了各种成熟的产品和架构,同时基于 Spring Boot 也使得整体的开发、配置、部署都非常的方便。
(2)Spring 系列的产品具备功能齐全、简单好用、性能优美、文档规范 等优点。

    
在这里插入图片描述
     可以看到,Eureka Server 是注册中心,把所有的服务都注册进来,做服务治理,然后通过 Ribbon 或者 Fein 进行负载均衡,它们都是做服务通信,Zuul 是服务网关,Hystrix 是熔断器,做服务容错,Config 做服务配置,Actuarot 做 服务监控,Zipkin 做服务跟踪,这几个都是核心组件。
    

二、服务治理

     服务治理的核心由三部分组成:
(1)注册中心
(2)服务消费者
(3)服务提供者
     就是说,在注册中心完成注册(比如 IP、端口等 )后,消费者就可以调用提供者的服务啦,可以联想 我们【消费者】在外卖平台 【注册中心】中浏览商家 【提供者】信息,然后下单,商家接下来就会配送到我们手里 。
     在 分布式系统架构 中,每个微服务在启动时,会将自己的信息存储在注册中心 这个过程叫 服务注册。服务消费者 从 注册中心 获取服务提供者的信息,通过这些信息调用服务提供者的服务,这个过程叫 服务发现

     在 Spring Cloud 中,使用 Eureka 来实现服务治理。 Eureka 是 Netflix 开源的 基于 REST 的服务治理方案,Spring Cloud 集成了 Eureka,提供服务注册和服务发现的功能,可以和基于 Spring Boot 搭建的微服务应用轻松完成整合,开箱即用。
    
     Spring Cloud Eureka 有两部分组成:
(1)Eureka Server——注册中心
(2)Eureka Client——客户端,所有要进行注册的微服务都是通过 Eureka Client 连接到 Eureka Server 完成注册的。
    
接下来,就来动手搭建一个 Spring Cloud 项目吧。
    首先,先创建父工程,引入 parent 、Spring Boot 和 Spring Cloud 依赖,pom.xml 如下:

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringCloudDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>
1、注册中心

在 父工程下新建 module eurekaServer :
在这里插入图片描述
引入它自己的依赖: (正是因为这个依赖 和 启动类上的 @EnableEurekaServer 注解,让它成为了 服务提供者 )

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>

创建 application.yml ,配置 Eureka Server 相关信息:

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url: 
      defultZone: http://localhost:8761/eureka/

属性说明

  • server.port:当前 Eureka Server 服务端口

  • eureka.client.register-with-eureka:是否将当前的 Eureka Server 服务作为客户端进行注册

  • eureka.client.fetch-registry:是否获取其他 Eureka Server 服务的数据

  • eureka.client.service-url.defaultZone:注册中心的访问地址

    
接下来,创建启动类,使用 @SpringBootApplication 注解声明 SpringBoot 服务入口,@EnableEurekaServer 注解声明该类是一个 EurekaServer 微服务,提供服务注册和服务发现功能,即 注册中心

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

    启动,这时报了个错:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘javax.servlet.Filter’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=httpTraceFilter)}

    根本原因是 Spring Boot 与 Spring Cloud 版本不兼容 😭,当时父工程的 spring-cloud-dependencies 中自动提示了 版本是 Finchley.SR2 ,我也就用的默认的这个,但是其实这样版本是不匹配的,SpringBoot2.2.X 及 2.3.X 集成 SpringCloud 可以用 Hoxton.SR1 。
在这里插入图片描述

运行结果:
在这里插入图片描述

    可以看到,还没有服务注册过来呢。如果把eureka: client: register-with-eureka属性改成 true,就是注册它自己,运行起来:
在这里插入图片描述

2、服务提供者

    服务提供者、服务消费者都是需要继承 Eureka Client 的。
    先创建 module,导入依赖(正是因为这个依赖,让它成为了 服务提供者 ):

	<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
    </dependencies>

然后在 application.yaml 中进行配置:

server:
  port: 8010
spring:
  application:
    name: provider
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

属性说明

  • spring.application.name : 当前服务注册在 Eureka Server 上的名称
  • eureka.client.service-url.defaultZone : 注册中心的访问地址
  • eureka.instance.prefer-ip-address : 是否将当前服务的 IP 注册到 Eureka Server

创建启动类,只需要 @SpringBootApplication 注解:

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

先启动注册中心,再启动服务提供者,可以看到:
在这里插入图片描述
    接下来,做个增删改查的实战。

我们在先 client 中创建一个实体类,需要先下载 lomback 插件, 然后 @Data 注解就可以生成 Getter、Setter、equals、hasCode、toString等方法;@AllArgsConstructor 注解可以添加一个构造函数,该构造函数含有所有已声明字段属性参数;@NoArgsConstructor 注解可以创建一个无参构造函数:

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {
    private long id;
    private String name;
    private int age;
}

再新建 respository 包,提供接口:

public interface StudentRepository {
    public Collection<Student> findAll();
    public Student findById(long id);
    public void saveOrUpdate(Student student);
    public void deleteById(long id);
}

实现类:

@Repository
public class StudentRespImpl implements StudentRepository{
    private static Map<Long,Student> studentMap;
    static{
        studentMap=new HashMap<>();
        studentMap.put(1L,new Student(1L,"张三",22));
        studentMap.put(2L,new Student(2L,"李四",21));
        studentMap.put(3L,new Student(3L,"王五",23));
    }

    @Override
    public Collection<Student> findAll() {
        return studentMap.values();
    }

    @Override
    public Student findById(long id) {
        return studentMap.get(id);
    }

    @Override
    public void saveOrUpdate(Student student) {
        studentMap.put(student.getId(), student);

    }

    @Override
    public void deleteById(long id) {
        studentMap.remove(id);
    }
}

提供控制类:(其实这里不是很规范的 REST 风格 )

@RestController
@RequestMapping("/student")
public class StudentHandler {
    @Autowired
    private StudentRepository studentRepository;

    @GetMapping("/findAll")
    public Collection<Student> findAll(){
      return studentRepository.findAll();
    }

    @GetMapping("/findById/{id}")
    public Student findById(@PathVariable("id") long id){
        return studentRepository.findById(id);
    }
    @PostMapping("/save")
    public void sava(@RequestBody Student student){
        studentRepository.saveOrUpdate(student);
    }

    @PutMapping("/update")
    public void update(@RequestBody Student student){
        studentRepository.saveOrUpdate(student);
    }

    @DeleteMapping("/delete/{id}")
    public void deleteById(@PathVariable("id") long id){
        studentRepository.deleteById(id);
    }

}

运行结果:
在这里插入图片描述
     这样,就创建了一个服务提供者,接下来,需要创建一个服务消费者,实现思路 是先通过 Spring boot 搭建一个微服务应用,再通过 Eureka Client 把它注册到注册中心 Eureka Server,成为一个服务消费者。那么 服务消费者 如何调用 服务提供者 的接口呢? 用 RestTemplate。
    

3、服务消费者

    先新建一个 module,因为 提供者 那边的方法有的返回值 是 Student 类型,所以需要先拷贝实体类 Student 。然后 导入 spring-cloud-starter-netflix-eureka-client 依赖:(正是因为这个依赖,让它成为了 服务消费者)

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>

然后创建配置文件:

server:
  port: 8020
spring:
  application:
    name: consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

接下来,创建启动类,同样需要创建 RestTemplate 对象:

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

还是需要拷贝实体类,然后提供控制类:

@RestController
@RequestMapping("/consumer")
public class ConsumerHandler {
    @Autowired
    RestTemplate restTemplate;
    @GetMapping("/findAll")
    public Collection<Student> findAll() {
        return restTemplate.getForEntity("http://localhost:8010/student/findAll",
                Collection.class).getBody();
    }

    @GetMapping("/findById/{id}")
    public Student findById(@PathVariable("id") long id){
        return restTemplate.getForEntity("http://localhost:8010/student/findById/{id}",
                Student.class,id).getBody();
    }

    @PostMapping("/save")
    public void save(@RequestBody Student student){
        restTemplate.postForEntity("http://localhost:8010/student/save",
                student, null).getBody();
    }

    @PutMapping("/update")
    public void update(@RequestBody Student student){
        restTemplate.put("http://localhost:8010/student/update", student);
    }

    @DeleteMapping("/deleteById/{id}")
    public void delete(@PathVariable("id") long id){
        restTemplate.delete("http://localhost:8010/student/delete/{id}",id);
    }
}

    这样,消费者注册到注册中心了,而且它是在访问提供者的接口,所以叫 “消费者”。这里调用 restTemplate 里的方法,之前学习 Spring Boot 的时候用过的。(博客链接🔗https://editor.csdn.net/md/?articleId=113433494 ) 这里如果是用 getForObject 方法,就不用调 getBody 啦。

再启动这个消费者,可以看到 :
在这里插入图片描述

运行结果:
在这里插入图片描述
    其他方法也验证了一遍,都是没问题的。
    

三、总结

(1)集群是从物理层面,让更多台服务器分摊高并发的压力;
    分布式是从软件层面,把一个大的项目拆分成若干个微服务,每个微服务是独立的,简化开发、松耦合。
    
(2)Spring Cloud 是微服务框架,是基于 Spring Boot 的。
    
(3)服务治理就是说 服务提供者、服务消费者 都在 注册中心 进行注册,然后提供者就可以调消费者的服务啦。在 Spring Cloud 中,使用 Eureka 来实现服务治理,Eureka 是基于 REST 的(也就是说遵循 HTTP 协议)。
     Eureka Server 是注册中心,使用 @EnableEurekaServer 注解作用于启动类上即可声明注册中心(需要导入 spring-cloud-starter-netflix-eureka-server 依赖);
     而 Eureka Client 只需要导入 spring-cloud-starter-netflix-eureka-client 依赖,启动类上只需要 @SpringBootApplication 注解即可。
     提供者和消费者都需要通过 Eureka Server 注册到注册中心,而 消费者 调用 提供者通过 RestTemplate 即可。提供者和消费者没有严格的界定,就是说,A 调用 B 的接口,可以看作 A 是提供者,B 是消费者。它们从代码层面看是没有太大区别的,提供者在控制层提供了接口,其实消费者除了内部调用了提供者的接口,它也对外提供了接口(所以也可以用作服务提供者)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值