SpringCloud学习源码:https://github.com/liuwen766/SpringCloudLearning.git
框架学习完?来实现一个小项目:https://blog.csdn.net/qq_41822345/article/details/104404451
SpingCloud框架技术是微服务架构的良好实现。简单来说,微服务架构风格想要开发一种由多个小服务组成的应用。每个服务运行于独立的进程,并且采用轻量级交互。多数情况下是一个HTTP的资源API。这些服务具备独立业务能力并可以通过自动化部署方式独立部署。这种风格可以最小化集中管理,从而可以使用多种不同的编程语言和数据存储技术。
-
单体应用存在的问题
1.随着业务的发展,开发变得越来越复杂。
2.修改、新增某个功能,需要对整个系统进行测试、重新部署。
3.一个模块出现问题,很可能导致整个系统崩溃。
4.多个开发团队同时对数据进行管理,容易产生安全漏洞。
5.各个模块使⽤用同一种技术进行开发,各个模块很难根据实际情况选择更合适的技术框架,局限性很大。
6.模块内容过于复杂,如果员工离职,可能需要很长时间才能完成工作交接。 -
单体应用架构与微服务架构
-
大型项目需要用到微服务架构(spring cloud)
分布式:就是将一个复杂问题拆分成若干个简单的小问题,将一个大型的项目架构拆分成若干个微服务来协同完成(软件设计层⾯面)。将一个庞大的工作拆分成若干个小步骤,分别由不同的人完成这些小步骤,最终将所有的结果进行整合实现大的需求。
微服务架构就是将单一程序开发成一个微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信,通常是HTTP RESTFUL API。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。 -
微服务的优点
1、各个服务的开发、测试、部署都相互独立,比如订单服务就可以拆分为一个单独的服务,而它的开发也可以不用依赖于其它服务,如果订单量很大,我们可以仅仅对其进行负载。
2、当一个新需求出现的时候,特别是在一个庞大的项目系统中,单体应用需要考虑各方面的问题:兼容性、影响度等,而使用微服务就可以直接跳过这些费时又烧脑的环节。
3、使用微服务对一个项目进行拆分之后,各个服务之间就消除了诸多限制,(解耦),只需要保证对外提供正确的接口即可,至于用什么编程语言、技术框架通通都不要去操心。 -
分布式服务治理的核心由三部分组成:服务提供者、服务消费者、注册中心。
在分布式系统架构中,每个微服务在启动时,将自己的信息存储在注册中心,叫做服务注册。服务消费者从注册中心获取服务提供者的网络信息,通过该信息调用服务,叫做服务发现。 -
Spring Cloud是一个全家桶式的技术栈,包含了很多组件。其最核心的五个组件: Eureka、Ribbon、Feign、Hystrix、Zuul。
①Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里。
②Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台。
③Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求。
④Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
⑤Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务。
更多相关:https://blog.csdn.net/forezp/article/details/83999882
https://blog.csdn.net/forezp/article/details/70148833
开始学习:
1.注册中心 Eureka Server 代码实现
所有要进⾏注册的微服务通过 Eureka Client 连接到 Eureka Server,完成注册。
- step1:新建maven工程,这是所有微服务模块的父工程。
- step2:在父工程pom.xml中添加依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- 解决 JDK9 以上版本没有 JAXB API jar 的问题,JDK9 以下版本不需要配置 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!-- 引入 Spring Cloud 的依赖,管理 Spring Cloud 各组件 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- step3:在父工程下新建一个Module模块,注册中心 eurekaServer。
- step4:在模块 eurekaServer 中的 pom.xml 中添加依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- step5:在resource包下新建配置文件 application.yml,添加 Eureka Server 相关配置。
(创建的是 yml 格式的配置文件,注意空格)
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
server.port :当前 Eureka Server 服务端口。
eureka.client.register-with-eureka :是否将当前的 Eureka Server 服务作为客户端进行注册。
eureka.client.fetch-fegistry :是否获取其他 Eureka Server 服务的数据。
eureka.client.service-url.defaultZone :注册中⼼心的访问地址。
- step6:创建启动类
package com.liuwen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 22:27
**/
@SpringBootApplication //声明该类是 Spring Boot 服务的⼊口。
@EnableEurekaServer //声明该类是一个 Eureka Server 微服务,提供服务注册和服务发现功能,即注册中心。
public class eurekaServerApplication {
public static void main(String[] args){
SpringApplication.run(eurekaServerApplication.class,args);
}
}
代码结构如下:
2.服务提供者 Provider 代码实现
- step1:在父工程下新建一个Module模块,服务提供者 eurekaProvider。
- step2:在模块 eurekaProvider 中的 pom.xml 中添加依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- step3:在resource包下新建配置文件 application.yml,添加 eurekaProvider 相关配置。
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。
- step4:创建启动类。
package com.liuwen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 22:50
**/
@SpringBootApplication
public class eurekaClientApplication {
public static void main(String[] args){
SpringApplication.run(eurekaClientApplication.class,args);
}
}
- step5:创建实体类。(对应数据库的表)
package com.liuwen.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 22:55
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- step6:创建repository。(DAO)
1.先创建接口层:
package com.liuwen.repository;
import com.liuwen.entity.Student;
import java.util.Collection;
public interface StudentRepository {
public Collection<Student> findAll();
public Student findById(long id);
public void saveOrUpdate(Student student);
public void deleteById(long id);
}
2.再创建接口实现层:
package com.liuwen.repository.impl;
import com.liuwen.entity.Student;
import com.liuwen.repository.StudentRepository;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 22:57
**/
@Repository
public class StudentRepositoryImpl implements StudentRepository {
private static Map<Long,Student> studentMap;
static{
studentMap = new HashMap<>(); //没有连接数据库,所以new三个数据用于测试。
studentMap.put(1L,new Student(1L,"西西",18));
studentMap.put(2L,new Student(2L,"刘稳",19));
studentMap.put(3L,new Student(3L,"康康",20));
}
@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);
}
}
- step7:创建Handler。(视图层请求的映射)
package com.liuwen.controller;
import com.liuwen.entity.Student;
import com.liuwen.repository.StudentRepository;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Collection;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 23:07
**/
@RestController
@RequestMapping("/student")
public class StudentHandler {
@Resource
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 save(@RequestBody Student student){
studentRepository.saveOrUpdate(student);
}
@PutMapping("/update")
private void update(@RequestBody Student student){
studentRepository.saveOrUpdate(student);
}
@DeleteMapping("/deleteById/{id}")
private void deleteById(@PathVariable("id") long id){
studentRepository.deleteById(id);
}
}
代码结构如下:
3.服务消费者 Consumer 代码实现
- step1:在父工程下新建一个Module模块,服务消费者 eurekaConsumer。
- step2:在模块 eurekaConsumer 中的 pom.xml 中添加依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- step3:在resource包下新建配置文件 application.yml,添加 eurekaConsumer 相关配置。
server:
port: 8030
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- step4:创建启动类。
package com.liuwen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-03-01 01:42
**/
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
@Bean //RestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
RestTemplate 是 Spring 框架提供的基于 REST 的服务组件,底层是对 HTTP 请求及响应进行了封装,提供了很多访问 RETS 服务的方法,可以简化代码开发。
- step5:创建实体类。(对应数据库的表)
package com.liuwen.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-02-29 22:55
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- step6:创建Handler。(视图层请求的映射)
package com.liuwen.controller;
import com.liuwen.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
/**
* @description: Good good study,day day up!
* @author: Liu Wen
* @create: 2020-03-01 01:44
**/
@RestController
@RequestMapping("/consumer")
public class ConsumerHandler {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return
restTemplate.getForEntity("http://localhost:8010/student/findAll", Collection.class).getBody();
}
@GetMapping("/findAll2")
public Collection<Student> findAll2(){
return restTemplate.getForObject("http://localhost:8010/student/findAll",Collection.class);
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return
restTemplate.getForEntity("http://localhost:8010/student/findById/{id}",Student.class,id).getBody();
}
@GetMapping("/findById2/{id}")
public Student findById2(@PathVariable("id") long id){
return
restTemplate.getForObject("http://localhost:8010/student/findById/{id}",Student.class,id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
restTemplate.postForEntity("http://localhost:8010/student/save",student,null)
.getBody();
}
@PostMapping("/save2")
public void save2(@RequestBody Student student){
restTemplate.postForObject("http://localhost:8010/student/save",student,null)
;
}
@PutMapping("/update")
public void update(@RequestBody Student student){
restTemplate.put("http://localhost:8010/student/update",student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
restTemplate.delete("http://localhost:8010/student/deleteById/{id}",id);
}
}
代码结构如下
五大核心组件
1.服务网关 Zuul
Zuul 是 Netflix 提供的一个开源的 API 网关服务器,是客户端和网站后端所有请求的中间层,对外开放一个 API,将所有请求导入统一的⼊口,屏蔽了服务端的具体实现逻辑,Zuul 可以实现反向代理的功能,在网关内部实现动态路路由、身份认证、IP 过滤、数据监控等。
功能:为要访问的服务端提供者屏蔽端口。负载均衡功能。
eg:要访问:http://localhost:8010/student/findAll,在不知道该服务端口的情况下可以输入:
http://localhost:8040/p/student/findAll 来完成访问。
2.负载均衡 Ribbon
Spring Cloud Ribbon 是一个负载均衡解决方案,Ribbon 是 Netflix 发布的负载均衡器,Spring Cloud
Ribbon 是基于 Netflix Ribbon 实现的,是一个用于对 HTTP 请求进行控制的负载均衡客户端。
在注册中心对 Ribbon 进行注册之后,Ribbon 就可以基于某种负载均衡算法,如轮询、随机、加权轮询、加权随机等自动帮助服务消费者调用接口,开发者也可以根据具体需求自定义 Ribbon 负载均衡算法。实际开发中,Spring Cloud Ribbon 需要结合 Spring Cloud Eureka 来使用,Eureka Server 提供所有可以调用的服务提供者列表,Ribbon 基于特定的负载均衡算法从这些服务提供者中选择要调用的具体实例。
3.Feign
Ribbon 是一个通用的 HTTP 客户端工具,Feign 是基于 Ribbon 实现的。
与 Ribbon 一样,Feign 也是由 Netflix 提供的,Feign 是一个声明式、模版化的 Web Service 客户端,它简化了开发者编写 Web 服务客户端的操作,开发者可以通过简单的接口和注解来调用 HTTP API,Spring Cloud Feign,它整合了 Ribbon 和 Hystrix,具有可插拔、基于注解、负载均衡、服务熔断等一系列便捷功能。
相比较于 Ribbon + RestTemplate 的⽅方式,Feign ⼤大简化了代码的开发,Feign 支持多种注解,包括Feign 注解、JAX-RS 注解、Spring MVC 注解等,Spring Cloud 对 Feing 进行了优化,整合了 Ribbon和 Eureka,从而让 Feign 的⽤用更加方便。
Feign的特点:
1、Feign 是一个声明式的 Web Service 客户端。
2、支持 Feign 注解、Spring MVC 注解、JAX-RS 注解。
3、Feign 基于 Ribbon 实现,使用起来更加简单。
4、Feign 集成了 Hystrix,具备服务熔断的功能。
4.容错机制 Hystrix
在不改变各个微服务调用关系的前提下,针对错误情况进行预先处理。
Hystrix 数据监控需要结合 Spring Boot Actuator 来使用,Actuator 提供了对服务的健康、数据统
计,可以通过 hystrix.stream 节点获取监控的请求数据,并提供了可视化的监控界⾯面。
特点:
1、服务隔离机制
2、服务降级机制
3、熔断机制
4、提供实时的监控和报警功能
5、提供实时的配置修改功能
5.服务跟踪 Zipkin
Zipkin 是一个可以采集并且跟踪分布式系统中请求数据的组件,让开发者可以更加直观的监控到请求在各个微服务所耗费的时间等,也提供可视化界面。
配置中心 Spring Cloud
Spring Cloud Config,通过服务端可以为多个客户端提供配置服务。Spring Cloud Config 可以将配置文件存储在本地,也可以将配置文件存储在远程 Git 仓库,创建 Config Server,通过它管理所有的配置文件。