SpringCloud第一版
SpringCloud
单体应用存在的问题
- 随着业务的发展,开发变得越来越复杂。
- 修改、新增某个功能,需要对整个系统进行测试、重新部署。
- 一个模块出现问题,很可能导致整个系统崩溃。
- 多个开发团队同时对数据进行管理,容易产生安全漏洞。
- 各个模块使用同一种技术进行开发,各个模块很难根据实际情况选择更合适的技术框架,局限性很大。
- 模块内容过于复杂,如果员工离职,可能需要很长时间才能完成工作交接。
分布式、集群
(运维部分)集群:一台服务器无法负荷高并发的数据访问量,那么就设置十台服务器一起分担压力,十台不行就设置一百台(物理层面)。很多人干同一件事情,来分摊压力。
(开发部分)分布式:将一个复杂问题拆分成若干个简单的小问题,将一个大型的项目架构拆分成若干个微服务来协同完成。(软件设计层面)。将一个庞大的工作拆分成若干个小步骤,分别由不同的人完成这些小步骤,最终将所有的结果进行整合实现大的需求。
与传统的单体应用开发,微服务开发的好处:
比如有3个团队,每个团队都可以有自己的一套代码,只需要维护自己的一个小项目即可。3个团队写了3个不同的项目,把这三个项目全部部署起来成为微服务,再通过微服务管理的机制把他们3个整合起来就可以实现微服务的分布式架构
这样的话,大大提高灵活性,如果自己是a的话不需要关心b和c, 只需要提供的REST接口对外可以调用即可,不需要关注a用的什么技术。相当于是系统架构层进行解耦,将一个复杂问题拆分成若干个简单的问题
微服务的不足:
为什么是SpringCloud
-
SpringCloud是完全基于SpringBoot,服务调用方式也是基于REST API接口,整合了各种成熟的产品和架构,同时基于SpringBoot也使得整体的开发,配置,部署都非常的方便
-
Spring系的产品功能非常的齐全,简单好用,性能优越,文档规范
SpringCloud会有一个注册中心,Eureka Servlet会把所有的微服务进行注册,拆成微服务之后,怎么进行通信,所有的微服务就需要一个管理者管理他们,即Eureka Servlet进行管理他们,这个把所有的服务都注册进去,Ribbon和FeinClient完成负载均衡,Zuul来实现网关。 -
服务治理:Eureka
-
服务通信:Ribbon
-
服务通信:Feign
-
服务网关:Zuul
-
服务容错:Hystrix
-
服务配置:Config
-
服务监控:Actuator
-
服务跟踪:Zipkin
主要是学习这些组件怎么使用,怎么进行整合 -
服务治理的核心又分3部分:服务提供者、服务消费者、注册中心
把单体开发的拆成3个不同的微服务,3个服务分别提供不同的功能,3个服务可以相互调用,提供服务的微服务叫做:服务提供者,调用其他服务的叫做服务消费者。服务提供者能够调用服务消费者,就是因为有注册中心。
首先服务提供者会在注册中心完成注册,比如:他的ip端口。服务消费者就可以在注册中心查到服务提供者的信息,类似点外卖,店铺在平台注册,消费者在平台点外卖 -
在分布式系统架构中,每个微服务在启动时,将自己的信息存储在注册中兴,叫做服务注册。
-
服务消费者从注册中心获取服务提供者的信息,通过改信息调用服务,叫做服务发现
Spring Cloud的服务治理使用Eureka来实现,Eureka是Nefilx开源的REST的服务治理方案,SpringCloud集成了Eureka,提供了注册和服务功能。可以和基于 Spring Boot 搭建的微服务应用轻松完成整合,开箱即用,Spring Cloud Eureka。
Spring Cloud Eureka
- Eureka Servlet ,注册中心
- Eureka Client,所有要进行注册的微服务通过Eureka Client连接到Eureka Server,完成注册。
Eureka Servlet注册中心代码实现
maven依赖问题,下载不了,改为阿里云
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
这个注意要选好,如果还是下载不了,重新删除依赖,之后重启id,重新贴一遍
- 新建maven工程
- 创建父工程,pom.xml.注意:父工程,是pom类型
<type>pom</type>
<!--父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
</parent>
<!--spring的web环境-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!--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>
在父工程中写一个子工程,同样也是一个maven工程,父工程有了springbootweb的依赖和springclouddependences的依赖,子工程只需要相关的组件对应的依赖即可。这个是注册组件,eureka-server
<!--要成为一个注册中心,eurekaserver相关组件-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件application.yml,添加Eureka Server相关的配置,在子模块汇总添加一个application.yml文件
# 端口
server:
port: 8761
# 注册中心的相关配置,eureka
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:访问注册中心的地址
- 创建启动类
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication // 声明该类是SpringBoot服务的入口
@EnableEurekaServer // 声明该类是Eureka Server微服务,提供服务注册和服务发现功能
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
- @SpringBootApplication`:声明该类是 Spring Boot 服务的入口。
- @EnableEurekaServer`:声明该类是一个 Eureka Server 微服务,提供服务注册和服务发现功能,即注册中心。
总结
1.新建父工程,在pom文件中加入公用的依赖
2.在父工程中创建一个子工程Module,在Module的pom文件中添加自己需要的组件依赖
3.在Spring Boot里面添加它的端口和Eureka相关的配置
4.最后创建启动类,在启动类添加注解,让当前这个工程成为一个Eureka Server
Eureka Client代码实现
- 创建Module,子模块eurekaClient,pom.xml
<!--引入相关依赖,这个就是客户端client的依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud </groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
<!--server是用来当注册中心的,而client是用来把服务注册到server的-->
- 创建配置文件application.yml,添加Eureka Client相关配置,此时client是一个服务提供者
# 给个访问的端口
server:
port: 8010
# 微服务的名字name,如:名字叫provider提供者,服务的提供者
spring:
application:
name: provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# 注册中心的地址配过来
insta nce:
prefer-ip-address: true
# 是否将当前的服务的ip注册到Eureka Server
属性说明:
- spring.application.name:当前服务注册在Eureka Server的名称
- eureka.client.service-url.defaultZone:注册中心的访问地址
- eureka.instance.prefer-ip-address:是否将当前服务的ip注册到Eureka Server
- 创建启动类 :如:ProviderApplication。提供者:
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProviderAppliaction {
public static void main(String[] args) {
SpringApplication.run(ProviderAppliaction.class,args);
}
}
把服务提供者注册到注册中心,第一步:启动注册中心的那个启动类,再启动服务提供者,就显示注册进去了
可以看成这个店在这个平台注册成功,可以把这个店展示出来了
一个具体的业务服务
在服务的提供者,写一个实体类
- entity实体类Stundent
package com.zhou.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- repository层,查询数据的接口
package com.zhou.repository.impl;
import com.zhou.entity.Student;
import com.zhou.repository.StudentRepository;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Repository
public class StudentRepositoryImpl implements StudentRepository {
// 使用map来模拟数据库,里面的泛型是对象类型,所以long数据类型改为Long对象类型
private static Map<Long,Student> studentMap;
static{
// 创建数据,使用hashMap来创建
studentMap = new HashMap<>();
// map类型的添加是put
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() {
// 返回全部的是values
return studentMap.values();
}
@Override
public Student findById(long id) {
// 操作的是模拟数据库中的数据,所以是get方法来获取对象
return studentMap.get(id);
}
@Override
public void saveOrUpdate(Student student) {
// 修改和添加都是通过id去找哪一个对象
studentMap.put(student.getId(),student);
}
@Override
public void deleteById(long id) {
studentMap.remove(id);
}
}
- controller层
package com.zhou.controller;
import com.zhou.entity.Student;
import com.zhou.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@RestController
@RequestMapping("/student")
public class StudentHandler {
// repository层的数据,注入进来
@Autowired
private StudentRepository studentRepository;
/*由于是RestController,返回的不是(视图)String,而是直接对象*/
@GetMapping("/findAll")
public Collection<Student> findAll(){
return studentRepository.findAll();
}
/*findById*/
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return studentRepository.findById(id);
}
// 添加是Post请求
@PostMapping("/save")
public void save(@RequestBody Student student){
studentRepository.saveOrUpdate(student);
}
// 修改的话是put
@PutMapping("/put")
public void put(@RequestBody Student student){
studentRepository.saveOrUpdate(student);
}
@DeleteMapping("/delete")
public void delete(@PathVariable("id") long id){
studentRepository.deleteById(id);
}
}
- 重新启动这个服务提供者,up是启动成功,如果是down则服务是停掉了
- 使用postman或者apiPost测试
同理测试put请求,delete请求
RestTemplate的使用
RestTemplate是Spring框架提供的基于REST的服务组件,底层是对HTTP请求及相应进行了封装,提供了很多访问REST服务的方法,可以简化代码的开发
- 如何使用RestTemplate
- 再建一个子模块,由于父类中有springboot的相关依赖,所以RestTemplate就不用写其他的依赖了
- 在resttemplate中怎么调用其他模块的方法?就通过resttemplate调用其他模块汇总的微服务的接口
- 首先把人家的实体类的包和实体类复制过来,然后写一个启动类
- 再写一个控制层,调用服务提供者的方法
package com.zhou.controller;
import com.zhou.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
@RestController
@RequestMapping("/rest")
public class RestHandler {
@Autowired // 把RestTemplate注入
private RestTemplate restTemplate;
@GetMapping("/findAll")
public Collection<Student> findAll(){
// 我们要去服务提供者里面调用方法查询
return restTemplate.getForEntity("http://localhost:8010/student/findAll",Collection.class).getBody();
}
}
全部启动测试这个方法,是否成立,先启动注册中心,再启动服务提供者,最后启动RestTemplate。RestTemplate只是一个普通的springboot项目是不会注册到注册中心的
看下服务提供者的方法
RestTemplate的方法查询,测试成功,
剩余的方法:
package com.zhou.controller;
import com.zhou.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;
@RestController
@RequestMapping("/rest")
public class RestHandler {
@Autowired // 把RestTemplate注入
private RestTemplate restTemplate;
// 第一种调用方法getForEntity方法查询
@GetMapping("/findAll")
public Collection<Student> findAll(){
// 我们要去服务提供者里面调用方法查询
return restTemplate.getForEntity("http://localhost:8010/student/findAll",Collection.class).getBody();
}
// 第二种调用方法的查询,使用getForObject
@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){
// 有个形参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){
//,null,Student.class注意:responseType是返回值类型,void的类型是null,然后这个student是实参,和上面的那个是不一样的,
// 上面id是实参,responseType是Student.class是返回值类型
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("/put")
public void put(@RequestBody Student student){
restTemplate.put("http://localhost:8010/student/put",student);
}
@DeleteMapping("/delete/{id}")
public void delete(@PathVariable("id") long id){
restTemplate.delete("http://localhost:8010/student/delete/{id}",id);
}
}
服务消费者consumer
- 创建消费者consumer是用来调用服务者提供的服务的,创建Maven工程,pom.xml
<!--添加酒依赖,netflix-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件application.yml
# 访问的端口
server:
port: 8020
spring:
application:
name: consumer
# 访问启动的包名
# 注册中心的访问地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# instance,prefer-ip-address把ip地址进行注册
instance:
prefer-ip-address: true
- 创建启动类,可以看成是注册到注册中心的一个微服务
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
// 消费者也是通过rest去调用其他微服务的方法所以注入ioc容器中
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 要使这个消费者去调用微服务中其他的方法,和上面的RestTemplate内容一样,把entity类中的东西给复制过来
- 创建controller层的handle类
package com.zhou.controller;
import com.zhou.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;
@RestController
@RequestMapping("/consumer")
public class ConsumerHandler {
// 这个也是通过Template相关的方法来调用其他微服务的方法的,
// 所以先把Template注入到consumer中,可以在启动类那里注入
@Autowired
private RestTemplate restTemplate;
// 第一种调用方法getForEntity方法查询
@GetMapping("/findAll")
public Collection<Student> findAll(){
// 我们要去服务提供者里面调用方法查询
return restTemplate.getForEntity("http://localhost:8010/student/findAll",Collection.class).getBody();
}
// 第二种调用方法的查询,使用getForObject
@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){
// 有个形参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){
//,null,Student.class注意:responseType是返回值类型,void的类型是null,然后这个student是实参,和上面的那个是不一样的,
// 上面id是实参,responseType是Student.class是返回值类型
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("/put")
public void put(@RequestBody Student student){
restTemplate.put("http://localhost:8010/student/put",student);
}
@DeleteMapping("/delete/{id}")
public void delete(@PathVariable("id") long id){
restTemplate.delete("http://localhost:8010/student/delete/{id}",id);
}
}
- restTemplate和consumer这两个包的区别是:restTemplate没有在注册中心注册,而consumer在注册中心注册了。所以consumer是一个微服务,是一个服务消费者
- 启动,先启动eurekaserver,查看注册中心是没有其他的微服务成功注册的。
再启动服务的提供者eurekaclient,provider服务的提供者,查看PROVIDER n/a (1) (1) UP (1) - localhost:provider:8010,已经注册进了。一个注册中心。
再启动consumer消费者,
并且consumer消费者可以去调用提供者的服务,可以调用他们的方法
测试,嗯可以
测试消费者的方法,和之前方式一样
服务网关
SpringCloud集成了Zuul组件,实现服务网关
可以减少客户端和服务器的交互性,提高系统的维护性
降低耦合性。具体是通过Zuul来实现
- 什么事Zuul?
Zuul是Netfilex提供的一个开源的API网关服务器,是客户端和网站后端的所有请求的中间层,对外开放一个API,将所有请求导入统一的入口,屏蔽了服务端的具体实现逻辑。Zuul可以实现反向代理的功能,在网关内部实现动态路由,身份认证,IP过滤,数据监控等。
- 创建Maven工程,pom.xml,一个是netflix-eureka-client和zuul
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件application.yml
server:
port: 8030
# 模块的名字application.name
spring:
application:
name: gateway
# 注册中心的路径
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
provider: /p/**
# 这个后面的这个是别名的意思,通过p就只可以知道是provider,端口就不用记,不太明白
# 给服务提供者设置映射provider设置映射,也就是给eurekaclient设置映射。给某一个微服务映射
zuul.routes.provider:给服务提供者:provider 设置映射,今后访问网关的时候,直接通过p就可以映射到provider
3. 创建启动类,ZuulApplication
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy // 包含了@EnableZuulServer,设置该类的网关的启动类
@EnableAutoConfiguration
// 可以帮助SpringBoot应用将所有符合条件的@configuration配置加载到当前的SpringBoot创并使用容器
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
-
@EnableZuulProxy
:包含了@EnableZuulServer
,设置该类是网关的启动类。 -
@EnableAutoConfiguration
:可以帮助 Spring Boot 应用将所有符合条件的@Configuration
配置加载到当前 Spring Boot 创建并使用的 IoC 容器中。
启动注册中心,然后启动服务的提供者,然后启动网关
通过p/做映射,别名,网关相当于是通过服务提供者去查询
- Zuul自带了负载均衡的功能(负载均衡:用多个服务来分担多种压力)由不同的实例来完成不同的请求的响应
修改provider代码,修改提供服务端的端口和实例(启动类)把启动类再加一个
// 加载yml中的port的端口,这种类似前端的,好像到都是使用$
@Value("${server.port}")
// 端口是字符串类型的
private String port;
// 把端口号给显示出来
@GetMapping("/index")
public String index(){
return "当前端口:"+this.port;
}
启动Provider服务提供类,后修改端口,再复制一个启动类并改名,再次启动
则说明有两个启动类都启动了,测试网关
两个实例在相互切换,可以减轻压力,多个实例承担多个请求。
Ribbon负载均衡
- 什么是Ribbon?
Spring Cloud Ribbon是一个负载均衡的解决方案,Ribbon是Netfilex发布的负载均衡器,Spring Cloud Ribbon是基于Netfilex Ribbon实现的,是一个用于对HTTP请求控制的负载均衡客户端。
在注册中心对Ribbon进行注册之后,Ribbon就可以基于某种负载均衡算法,如轮询,随机,加权随机等自动帮助服务消费者调用接口,开发者也可以根据具体的需求自定义Ribbon负载均衡算法。实际开发中,Spring Cloud Ribbon需要结合Spring Cloud Eureka来使用,Eureka Server提供所有可以调用的服务提供者,Ribbon基于特定的负载均衡算法从这些服务提供者中选择要调用的具体实例。
- 创建Module,pom.xml
<!--由于这个Ribbon也会注入到注册中心,所以也需要eureka-client-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 配置文件yml,配置文件一般没有波浪线,如果,检查一下是否缩进是否正确
server:
port: 8040
spring:
application:
name: ribbon # 起个微服务的名字
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # 注册中心的绑定
instance:
prefer-ip-address: true # 把ip地址注册进去
- 创建启动类
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class,args);
}
// 通过RestTemplate去调用服务者提供的服务
@Bean
@LoadBalanced // 声明一格基于Ribbon的负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- @loadBanlanced:声明一格基于Ribbon的负载均衡
- Rebbon负载均衡就可以去调用服务了,所以先写控制层controller,Handler。在Rebbon中去调用provider服务提供者的相关服务。把前面的student复制过来
package com.zhou.controller;
import com.zhou.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
@RestController
@RequestMapping("/ribbon")
public class RibbonHandler {
// 首先把RestTemplate注入,不同的微服务直接的api是通过这个来调用的
@Autowired
private RestTemplate restTemplate;
// 服务提供者的一个方法,查询所有
@GetMapping("/findAll")
public Collection<Student> findAll(){
// 由于provider这个端口已经被注入了,所以使用provider这个别名来替代端口也可以,里面的参数是url和返回值类型
return restTemplate.getForObject("http://provider/student/findAll",Collection.class);
}
@GetMapping("/index")
public String index(){
return restTemplate.getForObject("http://provider/student/index",String.class);
}
}
- 启动注册中心,启动服务提供者,先用之前的端口号,之后再换个端口号,再启动,再启动Robbon,每次访问都是交替去响应客户端的请求,他们对应的端口都不一样,这就是负载均衡
Feign
什么事Feign?
与Ribbon一样,Feign也是Netfilx提供的,Feign是一个声明式,模块化的Web Service客户端,它简化了开发这编写的Web的服务客户端的操作,开发者可以通过简单的接口和注解调用HTTP API,Spring Cloud Feign,它整合了Ribbon和Hystrix,具有可拔插,基于注解,负载均衡,服务熔断等一系列便捷功能。
相较于Ribbon+RestTemplate的方式,Feign大大简化了代码的开发,Feign支持多种注解,包括Feign注解,JAX-RS注解,Spring MVC注解等,Spring Cloud对Feing进行了优化,整合了Ribbon和Fureka,从而让Feign的使用更加便捷
- Ribbon和Feign的区别:Ribbon是一个通用的HTTP客户端的工具,Feign是基于Ribbon实现的。
- Feign的tedian
- Feign是一个声明式的Web Service客户端
- 支持Feign注解,Spring MVC注解,JAX-RS注解。
- Feign基于Ribbon实现,使用起来更加的简单
- Feign集成Hystrix,具备服务熔断的功能
使用:
- 创建maven,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>
- application.yml配置
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 创建启动类
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // Feign的注解。
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
- 创建feign的包,FeignClient和Ribbon不同,FeignClient直接在声明式接口写
package com.zhou.feign;
import com.zhou.entity.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
@FeignClient(value = "provider")
public interface FeignProviderClient {
// Feign是一个声明式接口,里面的方法访问的都是服务提供的方法,如:findAll和index
// 访问的具体哪一个服务,可以通过注解@FeignClient中之名是哪个微服务。
// 指明具体的服务后,可以通过@GetMapping
@GetMapping("/student/findAll")
Collection<Student> findAll();
@GetMapping("/student/index")
String index();
}
- 在controller层中调用这些接口
package com.zhou.controller;
import com.zhou.FeignApplication;
import com.zhou.entity.Student;
import com.zhou.feign.FeignProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
@RequestMapping("/feign")
public class FeignHandler {
// feign的接口的注入
@Resource
private FeignProviderClient feignProviderClient;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return feignProviderClient.findAll();
}
@GetMapping("/index")
public String index(){
return feignProviderClient.index();
}
}
启动,可以启动注册中心,提供服务的provider,和两个类似消费者的feign,ribbon的服务。这样,就有4个微服务了,feign和ribbon都可以通过负载均衡来实现查询的功能
熔断机制
当我们的某一个微服务出现问题,通过一种降级措施或者应急处理,保证服务不会整体崩溃。类似保险丝的功能
服务熔断,application.yml天际熔断机制
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
# 开启熔断机制
feign:
hystrix:
enabled: true
feign.hystrix.enabled是否开启熔断机制
- 创建FeignProviderClient接口的实现类FeignError,定义容器处理逻辑,通过@Component,注解将FeignError实例注入到IOC容器中。
package com.zhou.feign.impl;
import com.zhou.entity.Student;
import com.zhou.feign.FeignProviderClient;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component // 将FeignError实例注入到IOC容器中
public class FeignError implements FeignProviderClient {
@Override
public Collection<Student> findAll() {
return null;
}
@Override
public String index() {
return "服务器维护中";
}
}
package com.zhou.feign;
import com.zhou.entity.Student;
import com.zhou.feign.impl.FeignError;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
// fallback降级回到之前的映射
@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {
// Feign是一个声明式接口,里面的方法访问的都是服务提供的方法,如:findAll和index
// 访问的具体哪一个服务,可以通过注解@FeignClient中之名是哪个微服务。
// 指明具体的服务后,可以通过@GetMapping
@GetMapping("/student/findAll")
Collection<Student> findAll();
@GetMapping("/student/index")
String index();
}
- 在 FeignProviderClient 定义处通过
@FeignClient
的 fallback 属性设置映射。
测试:在关闭服务提供者后,访问成功
Hystrix容错机制
在不改变各个微服务调用关系的前提下,针对错误的情况进行预先处理。
- 设计原则
服务隔离机制
服务降级机制
熔断机制
提供实时的监控和报警功能
提供实时的配置和修改功能
Hystrix数据监控需要结合Spring Boot Actuator来使用,Actuator提供了对服务的健康,数据统计,可以通过hystrix.stream节点获取监控的请求数据,提供了可视化的监控界面
- 创建了Mavnen,pom.xml
- 创建配置yml
server:
port: 8060
spring:
application:
name: hystrix
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
endpoints:
web:
exposure:
include: 'hystrix.stream'
# 把这个节点中的数据全部展示出来,可以通过这个节点来获取箭筒的数据,提供了可视化的监控界面
- 创建启动类
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 声明熔断器
@EnableCircuitBreaker // 声明启用数据监控
@EnableHystrixDashboard // 声明启用可视化数据监控
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
}
- 把之前的实体类和Feign熔断给复制过来。
- 创建controller层,
package com.zhou.controller;
import com.netflix.discovery.converters.Auto;
import com.zhou.entity.Student;
import com.zhou.feign.FeignProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Collection;
@RestController
@RequestMapping("/hystrix")
public class HystrixHandler {
@Resource
private FeignProviderClient feignProviderClient;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return feignProviderClient.findAll();
}
@GetMapping("/index")
public String index(){
return feignProviderClient.index();
}
}
- 启动注册中心,启动提供服务的provider,启动hystrix微服务
- 测试:localhost:8060(端口号)/actuator/hystrix.stream,可以看到一直在监控
因为没有发送请求,所以一直是空白的,所以测试一个请求的url
启动成功之后,访问 http://localhost:8060/actuator/hystrix.stream
可以监控到请求数据,
访问 http://localhost:8060/hystrix
,可以看到可视化的监控界面,输入要监控的地址节点即可看到该节点的可视化数据监控。
Spring Cloud配置中心
Spring Cloud Config,通过服务端可以为多个客户端提供配置服务,Spring Cloud Config 可以将配置文件存储在本地,也可以嫁给你配置文件存储在远程的Git仓库,创建Config Server, 通过它管理的所有配置文件
本地文件系统
- 创建Maven工程,pom.xml。如创建:nativeconfigserver
<!--配置的依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- yml配置
server:
port: 8762
spring:
application:
name: nativeconfigserver
# 读取本地的配置
profiles:
active: native # native本地的
cloud: # 本地的配置的路径
config:
server:
native:
search-locations: classpath:/shared # 本地搜索的路径
注解说明:profiles.active:配置文件的获取方式
- cloud.config.server.native.search-locations: 本地配置文件存放的路径,从这个路径进行搜索。
- resources路径下创建了shared文件夹,并在此路径下创建configclient-dev.yml。
server:
port: 8070
foo: foo version 1
创建启动类:
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer // 声明配置中心
public class NativeConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(NativeConfigServerApplication.class,args);
}
}
- @EnableConfigServer:声明配置中心
本地的配置中心已经创建完成了,这个服务启动之后会给其他的服务提供一个返回配置文件的需求,那么接下来创建一个微服务来读取本地的另一个(shared中的)配置。
创建客户端读取本地的配置中心的配置文件
- 创建本地的客户端模块:nativeconfigclient模块。引入依赖,start-config表示我读取,config-server是提供服务
<!--读取配置依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建bootstrap.yml配置,名字必须叫这个,因为这个是提供给外面的配置,和默认的application.yml配置不一样。bootstrap.yml配置读取本地配置中心的相关信息。
# 名字
spring:
application:
name: configclient
#获取本地的配置,shared中的配置名就是 这里的name的名字-active的名字加一个-
profiles:
active: dev
cloud:
config:
# 注意这里是uri不是url这里的端口是访问提供者的端口
uri: http://localhost:8762
fail-fast: true
cloud.config.uri:本地 Config Server的访问路径
cloud.config.fail-fast:设置客户端有限判断Config Server获是否正常。
通过:config Server
通过sprig.application.name结合spring.profiles.active拼接目标配置文件名,configclient-dev.yml,去Config Server查找该文件。
创建启动类。
package com.zhou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class NativeConfigClient {
public static void main(String[] args) {
SpringApplication.run(NativeConfigClient.class,args);
}
}
controller层,NativeConfigHandler
package com.zhou.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/native")
public class NativeConfigHandler {
// 去获取本地的shared的配置
@Value("${server.port}")
private String port;
@Value("${foot}")
private String foo;
@GetMapping("/index")
public String index(){
return this.port+"-"+this.foo;
}
}
启动注册中心,启动server服务端,再启动nativeconfigclient。
Spring Cloud Config 远程配置
- 创建配置文件,上传至 GitHub
server:
port: 8070
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: configclient
- 创建 Config Server,新建 Maven 工程,pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件 application.yml
server:
port: 8888
spring:
application:
name: configserver
cloud:
config:
server:
git:
uri: https://github.com/southwind9801/aispringcloud.git
searchPaths: config
username: root
password: root
label: master
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
启动类:
package com.southwind;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
创建 Config Client
创建 Maven 工程,pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
创建 bootstrap.yml
spring:
cloud:
config:
name: configclient
label: master
discovery:
enabled: true
service-id: configserver
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
注解说明
spring.cloud.config.name
:当前服务注册在 Eureka Server 上的名称,与远程仓库的配置文件名对应。
spring.cloud.config.label
:Git Repository 的分支。
spring.cloud.config.discovery.enabled
:是否开启 Config 服务发现支持。
spring.cloud.config.discovery.service-id
:配置中心在 Eureka Server 上注册的名称。
启动类
package com.southwind;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class,args);
}
}
- Handler
package com.southwind.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloHandler {
@Value("${server.port}")
private String port;
@GetMapping("/index")
public String index(){
return this.port;
}
}
Spring Cloud Zipkin
Zipkin是一个可以采集并且跟踪分布式系统中请求数据的组件,让开发者可以更加直观的监控到请求在各个微服务所耗费的时间等,Zipkin:ZipKin Server,Zipkin Client。
- ZipKin Server,Zipkin服务端
- 创建Zipkin模块,maven工程,pom.xml
<dependencies>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.9.4</version>
</dependency>
</dependencies>
- 创建配置文件application.yml
server:
port: 9090
- 启动类。ZipkinApplication
package com.zhou.zipkin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.internal.EnableZipkinServer;
@SpringBootApplication
@EnableZipkinServer // 声明启动Zipkin Server
public class ZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinApplication.class,args);
}
}
@EnableZipkinServer:声明启动Zipkin Server
- 创建Zipkin Client,客户端
- 创建Maven工程,ZipkinClient。pom.xml
<!--客户端就只有zipkin-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 配置application.yml
server:
port: 8090
spring:
application:
name: zipkinclient
# sleuth是用来监控的,客户端启动网络的监控
sleuth:
web:
client:
enabled: true
sampler:
probability: 1.0
# zipkin访问客户端的地址
zipkin:
base-url: http://localhost:9090/
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
属性说明:
- Spring.sleuth.web.client.enabled: 设置开启请求跟踪
- spring.sleuth.sampler.probability:设置采样比例,默认是1.0
- spring.zipkin.base-url: http://localhost:9090/ 这个是Zipkin Server地址
package com.zhou.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ZipkinClientApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinClientApplication.class,args);
}
}
controller层,Handler方法
package com.zhou.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/zipkin")
public class zipkinClientHandler {
@Value("${server.port}")
public String port;
@GetMapping("/index")
public String index(){
return this.port;
}
}
启动,先启动注册中心,然后启动zipkin server,之后启动client