spring cloud

1.1集中式架构:

当网站流量很小时,只需一个应用,将所有的沟帮都部署在一起,以减少部署节点和成本,此时用于简化增删查改的工作量的数据访问框架(ORM)是影响项目开发的关键。
问题:
代码耦合,开发维护困难
无法针对不同模块进行针对性优化
无法水平扩展
单点容错率低,并发能力差

1.2垂直拆分

当访问量逐渐增大,单一应用无法满足需求,此时为了应对风发哦的并发和业务需求,根据业务功能对系统进行拆分
优点:系统拆分实现了流量分组,解决了并发问题
可以针对不同的模块进行优化
方便水平扩展,浮躁均衡,容错率提高
缺点:系统相互独立,会有很多重复开发的工作,影响开发效率

1.3分布式服务

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应更多地市场需求,此时,用于提高业务复用及整合的分布式调用是关键
优点:将基础服务进行了抽取,系统间的相互调用,提高了代码复用和开发效率
缺点:系统间耦合度变高,调用关系错综复杂,难以维护

1.4流动计算架构(SOA)

SOA:面向服务的架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需要增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率,此时,提高机器利用率的资源调度和治理中西(SOA)是关键

1.5微服务架构

1 微服务的特点:

1.单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责
2.微:微服务的服务拆分力度很小,例如一个用户管理就可以作为一个服务,每个服务虽小,但五脏俱全
3.面向服务:面向服务是说每个服务都要对外暴露Rest风格服务接口API 并不关心服务的技术实现 ,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest接口即可
4.自治:服务间相互独立,互不干扰
团队独立:每个服务都是一个独立的开发团队,人数不能过多。
  • 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉

  • 前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口

  • 数据库分离:每个服务都使用自己的数据源

  • 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护

    在这里插入图片描述

2服务的调用方式

2.1RPC 和HTTP

无论是微服务还是SOA,都面临服务间的相互调用。
常见的远程调用方式有以下2种:

  • RPC: Remote Produce Call 远程调用,类似的还有RMi,自定义数据格式,基于原生TCP通信速度快效率高.早期的webservice,现在的dubbo,都是RPC的典型代表
  • HTTP:http 其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在的客户客户端浏览器与服务端通信基本都采用HTTP协议,也可以用来进行远程服务的调用,缺点是消息封装臃肿,优势是对服务的提供和调用方法没有任何技术限定,自由灵活,更符合微服务的理念。
  • 现在热门的Rest风格,就可以通过http协议来实现
    如果公司全部采用Java技术栈,那么使用dubbo作为微服务架构是一个不错的选择
    相反,如果公司的技术栈多样化,而且更青睐spring家族,那么SpringCloud搭建微服务是不二之选

2.2Http客户端管理工具

既然微服务选择了http,那么就需要考虑自己来实现对请求和响应的处理,不过开源世界已经有很多http客户端工具,能够帮助我们做这些事情,例如:

  • HttpClient
  • OKHttp
  • HttpUriConnection
    (其中API各不相同)

2.3Spring的RestTemplate

Spring提供了一个RestTemplate模板工具类,对于HTtp客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定http客户端类型,而是进行了抽象,目前常用的3种都有支持:

  • HttpClient
  • OKHTtp
  • jdk原生的httpUriConnection(默认的)

3初始SpringCloud

微服务是一种架构方式,最终可定需要技术架构去实施。
微服务的实现方式有很多种,但是最火的莫过于Spring Cloud 。

  • 后台硬 :作为Spring家族的一员,有整个Spring 全家桶做靠山,背景十分强大
  • 技术强 :Spring 作为Java领域的齐纳呗,可以说是功力深厚,有强力的技术团队支撑.
  • 群众基础好:可以说大多数程序员的成长都伴随着Spring框架
  • 使用方便:Spring Cloud完全支持SpringBoot的开发,用很少的配置就可以完成微服务框架的搭建

3.1简介

SpringCloud是Spring旗下的项目之一,Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中
SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能,其主要涉及的组件包括:
Netfix:

  • 注册中心:Eureka
  • zuul:服务网关
  • Ribbon 负载均衡
  • Feign:服务调用
  • Hystix:熔断器
  • 等等

3.2.版本

因为Spring Cloud不同其他独立项目,它拥有很多子项目的大项目。所以它的版本是版本名+版本号 (如Angel.SR6)。

版本名:是伦敦的地铁名

版本号:SR(Service Releases)是固定的 ,大概意思是稳定版本。后面会有一个递增的数字。

所以 Edgware.SR3就是Edgware的第3个Release版本。

4微服务场景

首先,我们需要模拟一个服务调用的场景,搭建两个工程:(服务提供方)和 服务调用方)。

服务提供方:使用mybatis操作数据库,实现对数据的增删改查;并对外提供rest接口服务。

服务消费方:使用restTemplate远程调用服务提供方的rest接口服务,获取数据。

4.1服务提供者

新建一个项目:对外提供根据id查询用户的服务

4.1.1使用Spring脚手架创建工程

借助于spring提供的快速搭建工具:
在这里插入图片描述
在这里插入图片描述
next --> 添加web依赖:
添加mybatis依赖:
Next --> 填写项目位置:
生成的项目结构,已经包含了引导类(itcastServiceProviderApplication):
依赖也已经全部自动引入:
当然,因为要使用通用mapper,所以我们需要手动加一条依赖

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

4.1.2编写代码

4.1.2.1配置
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sc?serverTimezone=UTC
    driver: com.mysql.cj.jdbc.Driver
    username: root
    password:
#进行配置集成mybatis自动扫描到自定义POJO
mybatis:
  type-aliases-package: cn.wang.service.pojo



4.1.2.2实体类
package cn.wang.service.pojo;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;
@Table(name = "user")
public class User {
        private static final long serialVersionUID = 1L;
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        // 用户名
        private String userName;
        // 密码
        private String password;
        // 姓名
        private String name;
        // 年龄
        private Integer age;
        // 性别,1男性,2女性
        private Integer sex;
        // 出生日期
        private Date birthday;
        // 创建时间
        private Date created;
        // 更新时间
        private Date updated;
    }
4.1.2.3.UserMapper

import cn.wang.service.pojo.User;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.common.Mapper;
@Component(value = "userMapper")
public interface UserMapper extends Mapper<User> {
}

4.1.2.4.UserService
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return this.userMapper.selectByPrimaryKey(id);
    }
}
4.1.2.5.UserController

添加一个对外查询的接口:

package cn.wang.service.controller;
import cn.wang.service.pojo.User;
import cn.wang.service.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
 @RestController注解等价于@ResponseBody + @Controller。@RestController和@Controller的共同点是都用来表示Spring某个类是否可以接收HTTP请求,二者区别: @RestController无法返回指定页面,而@Controller可以;前者可以直接返回数据,后者需要@ResponseBody辅助。下面详细分析。

① 是否可以返回页面

  答:@RestController无法返回指定页面,而@Controller可以。
  解析:对于Controller, 如果只是使用@RestController注解,则其方法无法返回指定页面,此时配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是 return 里的内容。 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
② 返回内容
  如果需要返回JSON,XML或自定义mediaType内容到页面,@RestController自己就可以搞定,这个注解对于返回数据比较方便,因为它会自动将对象实体转换为JSON格式。而@Controller需要在对应的方法加上@ResponseBody注解。
 */
@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("{id}")//@GetMapping用于处理请求方法的GET类型,@ PostMapping用于处理请求方法的POST类型等。
    public User queryUserById(@PathVariable("id") Long id){//接收请求路径中占位符的值,
        //通过 @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)
        return this.userService.queryUserById(id);
    }
}
4.1.2.6启动接口
package cn.wang.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
/*mapper和dao作用相同*/
@SpringBootApplication//是一个组合注解,用于快捷配置启动类,自动配制
//可以配置多个启动类,但启动时需要选择哪个类作为启动类来启动项目
@MapperScan("cn.wang.service.mapper")//通过使用@MapperScan可以指定要扫描的Mapper类的包的路径,就可以不用在mapper接口上加mapper注解了
public class WangServiceProvideApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(WangServiceProvideApplication.class, args);
    }

}

/*Spring Boot核心注解@SpringBootApplication
一、作用
  @SpringBootApplication是一个组合注解,用于快捷配置启动类。

二、用法
  可配置多个启动类,但启动时需选择以哪个类作为启动类来启动项目。

三、拆解
1.拆解
   此注解等同于@Configuration+@EnableAutoConfiguration+@ComponentScan的合集,详见https://docs.spring.io/spring-boot/docs/1.5.5.BUILD-SNAPSHOT/reference/htmlsingle/#using-boot-using-springbootapplication-annotation

2.源码
  查看@SpringBootApplication注解的定义,部分源码如下:

复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}),
    @Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )})
public @interface SpringBootApplication {
复制代码


3.源码分析
前四个注解是元注解,用来修饰当前注解,就像public类的修饰词,无实际功能。后三个注解是真正起作用的注解,下面逐一解释。

@SpringbootConfiguration
  说明这是一个配置文件类,它会被@ComponentScan扫描到。进入@SpringBootConfiguration源码发现它相当于@Configuration,借此讲解下。
  提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的Spring配置类,可用来替代相应的xml配置文件。

复制代码
@Configuration
public class Conf {
    @Bean
    public Car car() {
        Car car = new Car();
        car.setWheel(wheel());
        return car;
    }
    @Bean
    public Wheel wheel() {
        return new Wheel();
    }
}
复制代码
等价于

<beans>
    <bean id = "car" class="com.test.Car">
        <property name="wheel" ref = "wheel"></property>
    </bean>
    <bean id = "wheel" class="com.test.Wheel"></bean>
</beans>
@Configuration的注解类标识这个类可使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象被注册为在Spring应用程序中上下文的bean。

@ComponentScan
  会自动扫描指定包下全部标有@Component的类,并注册成bean,当然包括@Component下的子注解:@Service,@Repository,@Controller;默认会扫描当前包和所有子包。

@EnableAutoConfiguration
  根据类路径中jar包是否存在来决定是否开启某一个功能的自动配置。

tips:exclude和excludeName用于关闭指定的自动配置,比如关闭数据源相关的自动配 */

4.1.3启动测试

访问接口:http://localhost:8080/user/1

4.2服务调用者

搭建wang-service-consumer服务消费方工程。

4.2.1.创建工程

同上只需web依赖即可

4.2.2配置文件

server:
  port: 8081

4.2.3引导类编写

package cn.wang.service;
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 WangServiceConsumerApplication {

    @Bean//Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
   // SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
    //远程接口用来进行远程访问
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(WangServiceConsumerApplication.class, args);
    }
}

4.2.4实体类pojo

import java.io.Serializable;
import java.util.Date;

public class User {
        private Long id;
        // 用户名
        private String username;
        // 密码
        private String password;
        // 姓名
        private String name;

        // 年龄
        private Integer age;

        // 性别,1男性,2女性
        private Integer sex;

4.2.4controller类编写

package cn.wang.service.controller;

import cn.wang.service.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

@Controller
@RequestMapping("con/user")
public class UserController {
    //首先注入远程访问接口
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id")Long id){
        //远程调用服务方的接口
       return  this.restTemplate.getForObject("http://localhost:8080/user/"+id,User.class);

    }
}

测试

在这里插入图片描述

4.3存在的问题

在consumer中,把URL地址编码到了代码中,不方便后期维护
consumer中需要记忆provider的地址,如果出现变更,可能得不到通知,地址将失效
consumer不清楚提供者的状态,服务宕机也不知道
provider只有一台服务,不具备高可用性
即便provider形成集群,consumer还需要自己实现负载均衡

其实上面说的问题,概括一下就是分布式服务必然要面临的问题:

  • 服务管理
    • 如何自动注册和发现
    • 如何实现状态监管
    • 如何实现动态路由
  • 服务如何实现负载均衡
  • 服务如何解决容灾问题
  • 服务如何实现统一配置
  • 解决:

5.Eureka注册中心

5.1认识Rureka

第一个问题:服务的管理

问题分析

在provider对外提供服务,需要对外暴露自己的地址。而consumer(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。

Eureka做什么?

eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。同时,服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。
这就实现了服务的自动注册、发现、状态监控。

  • Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
  • 提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
  • 消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
  • 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态

5.2案例

5.2.1搭建EurekaServer

新建moudle
在这里插入图片描述
三步:
1引入组件的启动器
2覆盖默认配置
3.在引导类上添加注解,开启相关组件的注解

覆盖默认配置

server:
  port: 8086
spring:
  application:
    name: wang-eureka #将来作为微服务名称注入到eureka容器中 # 应用名称,会在Eureka中显示

eureka:
  client:
    service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
      defaultZone: http://localhost:${server.port}//eureka

3引导类加入注解

package cn.wang.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer//启用eureka服务端// 声明当前springboot应用是一个eureka服务中心
public class WangEurekaApplication {

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

}

启动服务,并访问:http://127.0.0.1:8086
在这里插入图片描述

5.2.2.注册到Eureka

注册服务,就是在服务上添加eureka的客户端依赖,客户端代码会自动把服务注册到EurekaServer中
修改provider工程

  1. 在pom.xml中,添加springcloud的相关依赖。
  2. 在application.yml中,添加springcloud的相关依赖。
  3. 在引导类上添加注解,把服务注入到eureka注册中心。
5.2.2.1.provider pom.xml

在eurkea客户端即服务提供中引入依赖

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

添加版本

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>

然后通过依赖管理进行管理版本号

<!--统一管理依赖的版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

接下来把客户端即服务的提供方的信息注册到eureka
-----添加配置

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sc?serverTimezone=UTC
    driver: com.mysql.cj.jdbc.Driver
    username: root
    password:
  application:
    name: service-provider #将来作为作为微服务的名称
#进行配置集成mybatis自动扫描到自定义POJO
mybatis:
  type-aliases-package: cn.wang.service.pojo
#配置eureka的信息
eureka:
  client:
    service-url:  #EurekaServer地址
      defaultZone: http://localhost:8086/eureka

接下来在启动入口引导类进行开启启动注解

package cn.wang.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import tk.mybatis.spring.annotation.MapperScan;
/*mapper和dao作用相同*/
@SpringBootApplication//是一个组合注解,用于快捷配置启动类,自动配制
//可以配置多个启动类,但启动时需要选择哪个类作为启动类来启动项目
@MapperScan("cn.wang.service.mapper")//通过使用@MapperScan可以指定要扫描的Mapper类的包的路径,就可以不用在mapper接口上加mapper注解了
@EnableDiscoveryClient//启用eureka的客户端
public class WangServiceProvideApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(WangServiceProvideApplication.class, args);
    }

}

注意:

  • 这里我们添加了spring.application.name属性来指定应用名称,将来会作为应用的id使用。

进行访问:
注册成功:
在这里插入图片描述

5.2.3从eureka中获取服务(即另一个客户端)

方法与提供服务的客户端类似
在consumer中修改pom.xml引入依赖

添加版本

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>

<!--引入客户端依赖-->
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

<!--统一管理依赖的版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

然后在spring主配置文件中配置客户端的信息

server:
  port: 8081
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8086/eureka

然后在引导类中加注解

package cn.wang.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class WangServiceConsumerApplication {

    @Bean//Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
   // SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
    //远程接口用来进行远程访问
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(WangServiceConsumerApplication.class, args);
    }

}

在这里插入图片描述
解决地址问题

package cn.wang.service.controller;

import cn.wang.service.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Controller
@RequestMapping("con/user")
public class UserController {
    //首先注入远程访问接口
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;//包含了拉取的所有服务信息

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id")Long id){
        List<ServiceInstance> instances= discoveryClient.getInstances("service-provider");
        ServiceInstance instance=instances.get(0);

        //远程调用服务方的接口
       return  this.restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/user/"+id,User.class);

    }
}

5.3Eureka 详解

5.3.1.基础架构

Eureka架构中的三个核心角色:

  • 服务注册中心

    Eureka的服务端应用,提供服务注册和发现功能,就是刚刚我们建立的wang-eureka。

  • 服务提供者

    提供服务的应用,可以是SpringBoot应用,也可以是其它任意技术实现,只要对外提供的是Rest风格服务即可。本例中就是我们实现的
    wang-service-provider。

  • 服务消费者

    消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。本例中就是我们实现的wangt-service-consumer。

5.3.2高可用的Eureka Server

Eureka Server即服务的注册中心,在刚才的案例中,我们只有一个EurekaServer,事实上EurekaServer也可以是一个集群,形成高可用的Eureka中心。

服务同步

多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
copy一个Eureka
在这里插入图片描述
进行相互注册
在这里插入图片描述
在这里插入图片描述
即使挂掉一个也不影响
4)客户端注册服务到集群

因为EurekaServer不止一个,因此注册服务的时候,service-url参数需要变化:

eureka:
  client:
    service-url: # EurekaServer地址,多个地址以','隔开
      defaultZone: http://127.0.0.1:8086/eureka,http://127.0.0.1:8087/eureka

以下配置仅在开发环境中使用

# 关闭注册中心的自我保护机制,防止已关闭的实例无法从注册中心剔除
#eureka.server.enable-self-preservation=false

5.3.3.服务提供者

服务提供者要向EurekaServer注册服务,并且完成服务续约等工作

服务注册

服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-eureka=ture参数是否正确
事实上默认就是true,如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的与数据信息,Eureka server 会把这些信息保存到一个双层的Map结构中

  • 第一层map的key就是服务的id,一般是配置中的spring.application.name属性
  • 第二层map的key是服务的实例id,一般是host+serviceid+port例如:localhost:user-service:8081
  • 值则是服务的实例对象,也就是说一个服务可以同时开启多个不同的实例,形成集群

服务续约

在注册服务完成后,服务提供者会维持一个心跳(定时向Eurekaserver发起Rest请求),告诉Eurekaserver—我还活着,这个就是服务的续约(renew):
有两个重要的参数可以修改服务续约的行为

  instance:
    lease-renewal-interval-in-seconds: 5 #心跳时间---服务的续约时间间隔,默认为30秒
    lease-expiration-duration-in-seconds: 15 #过期时间----服务的失效时间,默认为90

也就是说,默认情况下每间隔30秒服务会向注册中心发送一次心跳,证明自己还活着,如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境下不修改,默认即可
但是在开发时,这个值比较大,经常我们关掉一个服务,会发现Eureka依然认为服务还存活着,所以在开发阶段适当调小

5.3.4服务的消费者

获取服务列表

当服务消费者启动时,会检测eureka.client.fetch-registry=true 参数的值,如果为true,则会拉取eurekaserver服务的列表只读备份,然后缓存在本地,并且每隔30秒会重新获取并更新数据,通过下面的参数来修改

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8086/eureka,http://localhost:8087/eureka
    registry-fetch-interval-seconds: 5 

生产环境中,不需要修改这个值
开发中为了能快速得到服务的最新状态,可以设置小一点

5.3.5失效剔除和自我保护

服务下线

当服务进行正常关闭操作时,它会触发一个服务下线的Rest请求给EurekaServer,告诉注册中心我要下线了
服务中心接受到请求之后,将该服务设置为下线状态

失效剔除

有些时候,当我们的服务提供方并不一定会正常下线,可能因为内存溢出,网络故障等原因导致服务无法正常工作,EurekaServer 需要将这样的服务剔除服务列表,因为它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除
可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒,生产环境不要修改
开发阶段适当调整为10秒左右

    eviction-interval-timer-in-ms:  5000

关停服务的时候—宁愿放过1千,不杀一个微服务

  server:
    enable-self-preservation: true
    # 以下配置仅在开发环境中使用
    # 关闭注册中心的自我保护机制,防止已关闭的实例无法从注册中心剔除
    #eureka.server.enable-self-preservation=false
    eviction-interval-timer-in-ms:  5000 #剔除无效连接间隔时间

6负载均衡Ribbon

在实际环境中,会有开启很多个service-provider 的集群,此时获取的服务列表就会有很多个
到底访问哪一个呢(部署多个相同服务)
这种情况下需要编写负载均衡算法,在多个实例列表中进行选择
在Eureka中已经集成了负载均衡组件:RIbbon 简单修改代码
什么是Ribbon:
Ribbon是Netflix发布的负载均衡器,有助于控制HTTP和TCP客户端的行为,为Ribbon配置服务提供者地址列表后,Ribbon就可以基于某种负载均衡算法,自动的帮助服务消费者去请求.Ribbon提供了很多的负载均衡算法,例如轮询\随机等
也可以自定义实现负载均衡算法

6.1启动两个服务实例

1.引入启动器(引入依赖,在消费方进行负载均衡)—在Eureka中集成了ribbon
2.覆盖默认配置(Ribbon不需要覆盖任何配置)
3.在引导类启用(这个比较特别,不是在引导类启用,而是在template启用)
在这里插入图片描述
在这里插入图片描述
在controller中进行修改代码
在这里插入图片描述
SpringBoot也帮我们提供了修改负载均衡规则的配置入口,在itcast-service-consumer的application.yml中添加如下配置:

server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
service-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。

7Hystrix

7.1简介

Hystrix英文的意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制
Hystrix也是Netflix公司的一款组件
Hystrix是开源的一个延迟和容错库,用于隔离访问远程服务,第三方库,防止出现级联失败

7.2雪崩问题

微服务中,服务间调用关系错综复杂,一个请求可能学要你调用多个微服务接口才能实现,会形成错综复杂的调用线路:

Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速失败,判定时间
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则进行降级处理,什么是服务降级?

服务降级----优先保证核心服务,而非核心服务不可用或减弱可用

用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到西永崩溃,至少可用看到一个执行结果(例如返回一个有好的提示信息)
服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其他服务没有响应
出发Hystrix降级的情况L

  • 线程池已满
  • 请求超时

Hystix解决雪崩问题的手段有两个:

  • 线程隔离
  • 服务熔断

7.3

7.3.1(在调用服务的一方添加Hystrix熔断)

1引入启动依赖

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

2–不用修改配置可以用

第三步–在引导类中加入注解
在这里插入图片描述
四:在代码中
在这里插入图片描述

7.3.2测试

停掉服务提供方
进行访问
请求超时得到熔断信息
在这里插入图片描述

7.3.3定义全局熔断方法

在这里插入图片描述
在这里插入图片描述

注意:全局熔断所有熔断方法返回值一样,参数去掉
在这里插入图片描述
总结:
降级:
1引入Hystrix启动器
2.覆盖配置
3.在引导类上加注解
4.定义熔断的方法(要和被熔断的方法返回值和参数列表一致)----全局把参数去掉
5.声明被熔断的方法

7.4设置熔断超时

在上述例子中请求一秒就会返回错误信息,因为默认的超时时长是1,我们可以通过配置修改这个值
通过Hystrix.command.default.execution.iSOlation.thread.timeoutInmilliseconds来设置Hystrix超时时间,该配置没有提示

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 #设置超时时间为6000ms

在这里插入图片描述

7.5服务熔断

7.5.1熔断原理

熔断器,也叫断路器,其英文单词为:circuit Breaker
熔断机制的原理很简单,像是家里的电路熔断器,如果是电路发生短路能立刻熔断电路,避免发生灾难。在分布式系统中应用这一模式之后,服务调用方可以自己进行判断某些服务反应慢或者存在大量超时的情况时,能够主动熔断,防止整个系统被拖垮。
不同电路熔断只能断不能自动重连,Hystrix可以实现弹性容错,当情况好转之后,可以自动重连。
通过断路的方式,可以将后续请求直接拒绝掉,一段时间后允许部分请求通过,如果调用成功则回到电路闭合状态,否则继续断开
熔断状态机3个状态

  • Close:关闭状态,所有请求正常访问
  • open:打开状态,所有请求都会被降级。Hystrix会针对请求情况计数,当一定时间内失败请求百分比达到阈值,则熔断触发,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次
  • Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5秒)。随后断路器就会自动进入半开状态。此时会释放部分请求通过,这些请求都是健康的,则会完全关闭断路器,否则继续保持打开再次进行休眠计时

默认的熔断触发要求比较高,休眠时间窗较短,为了测试方便,可以通过配置修改熔断策略:
在这里插入图片描述

  • requestVolunmeThreshold:触发熔断的最小请求次数,默认20
  • errorThresholdPercentage:触发熔断的失败请求最小占比,默认是50%
  • sleepWindowInMilliseconds:休眠时长,默认是5000毫秒

8Feign(飞嗯)—远程调用

Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样 ,不用再进行拼装url,拼装参数等
一切交给Feign去做’
在这里插入图片描述

8.2

1配置启动器(导入依赖)

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

2.配置(不需)
3.在引导类加注解
在这里插入图片描述

启用feign之后不再使用restTemplate

在这里插入图片描述
controller也不同于上述

  1. 创建一个client包下USerClient接口
  2. 在接口上加注解
package cn.wang.service.client;

import cn.wang.service.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient("service-provider")//指定微服务的id
public interface UserClient {
    //调用服务的接口
    @GetMapping("user/{id}")//@GetMapping用于处理请求方法的GET类型,@ PostMapping用于处理请求方法的POST类型等。
    public User queryUserById(@PathVariable("id") Long id);
}

3.编码controller
注入:
在这里插入图片描述

    public String queryUserById(@RequestParam("id")Long id){
      
        return this.userClient.queryUserById(id).toString() ;
    }

访问成功
在这里插入图片描述

8.3负载均衡

Feign本身已经集成了ribbon依赖和自动配置

8.4 Hystrix支持

Feign默认也集成了Hystrix,但是默认情况下是关闭的。需要通过参数来开启(在服务调用者配置中)

feign:
  hystrix:
    enabled: true #开启feign的熔断功能

但是Feign中的Fallback不像Hystrix中那么简单

  1. 首先,要定义一个类UserClientFallback,实现上述编写的UserClient作为Fallback的处理类,并加入到spring容器中
package cn.wang.service.client;

import cn.wang.service.pojo.User;
@Component//注入到spring容器中
public class UserClientFallback  implements UserClient{
    @Override
    public User queryUserById(Long id) {
       User user=new User();
       user.setUserName("服务器正忙,请稍候在试");
       return user;
    }
}
  1. 在UserClient接口中配置注解
package cn.wang.service.client;

import cn.wang.service.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value="service-provider",fallback = UserClientFallback.class)//指定微服务的id,和熔断类
public interface UserClient {
    //调用服务的接口
    @GetMapping("user/{id}")//@GetMapping用于处理请求方法的GET类型,@ PostMapping用于处理请求方法的POST类型等。
    public User queryUserById(@PathVariable("id") Long id);
}

在这里插入图片描述

9zuul网关

1解决服务无状态的特点
2.解决无法直接复用已有接口

服务网关是微服务架构中一个不可或缺的部分,通过服务网关统一向外系统提供Rest API的过程中,除了具备服务器路由、均衡负载功能之外,他还具备了权限控制等功能。SpringCloud Netflix中的Zuul就担任了这样的一个角色,为微服务架构提供了前门保护作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主题能够具备更高的可复用性和可测试性

9.1简介

在微服务架构中,zuul就是网关
在这里插入图片描述
路由:分发给不同的微服务(服务名)
负载均衡:同一个服务的不同实例

9.2Zuul加入后的架构

在这里插入图片描述
不管是来自客户端(PC或移动端)的请求,还是服务内部的调用,一切对对服务的内部请求都会经过这个Zuul网关,然后再由网关实现鉴权,动态路由等等操作,Zuul就是我们服务的统一入口

9.3zuul入门

9.3.1新建模块

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.3.2引入依赖

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

9.3.3覆盖默认配置

server:
  port: 8089
spring:
  application:
    name: wang-zuul

9.3.4在引导类加注解

package cn.wang.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy//启用zuul网关组件
public class WangZuulApplication {

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

9.3.5配置服务路由

server:
  port: 8089
spring:
  application:
    name: wang-zuul

zuul:
  routes:
    service-provider: #路由名称,可以随便写,习惯上服务名
      path: /service-provider/** #服务是这个名字开头路由到下面的路径
      url: http://localhost:8080

访问:
在这里插入图片描述

9.4把zuul注册到Eureka中做到服务分发

1.引入Eureka的依赖

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

2覆盖默认配置

eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8086/eureka

3.在引导类启用客户端,添加注解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第三种配置方式
在这里插入图片描述
访问:
在这里插入图片描述

终极配置—不配置,默认就是服务id开头路径

服务名+访问路径

一般使用第三种:

可以加前缀
prefix: /api

9.5过滤器

Zuul作为网关的其中一个重要的功能,就是实现请求的鉴权,而这个动作我们往往是通过Zuul提供的过滤器来实现的

9.5.1ZuulFilter

ZuulFilter是过滤器的顶级父类,有四个最重要的方法

9.5.2过滤器执行生命周期

在这里插入图片描述
正常流程:
请求到达首先会经过pre类型的过滤器,而后到达route类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器,而后返回响应
返回值决定优先级—
创建类继承ZuulFilter

package cn.wang.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
@Component
public class LoginFilter extends ZuulFilter {
    /*
     * 过滤器的类型:Pro,route post error
     * */
    @Override
    public String filterType() {
        return "pre";
    }

    /*
    执行顺序,返回值越小优先级越高
     */
    @Override
    public int filterOrder() {
        return 10;
    }

    /*
    是否执行该过滤器
    true:执行run方法
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /*
    编写过滤器的业务逻辑
     */
    @Override
    public Object run() throws ZuulException {
        //1初始化context上下文对象,servlet spring
        RequestContext context = RequestContext.getCurrentContext();
        //1获取request对象
        HttpServletRequest request = context.getRequest();

        //获取参数
        String token = request.getParameter("token");
        if (StringUtils.isBlank(token)) {//为空判断
            //拦截,不转发请求
            context.setSendZuulResponse(false);
            //响应状态码,身份未认证
            context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
            context.setResponseBody("请求失败  request error");
        }
        //返回值为null,就代表该过滤器什么都不做
        return null;
    }
}

在这里插入图片描述

9.6负载均衡和熔断

Zuul中默认就已经完成了Ribbon负载均衡和Hystrix熔断机制,但是所有的超时策略都是走默认值,比如熔断超时时间只有1s,很容易就触发了,因此建议手动配置

spring:
  application:
    name: abcmsc-zuul-depart
 
 
 
ribbon:        #设置ribbon的超时时间小于zuul的超时时间
  ReadTimeout: 10000
  ConnectTimeout: 10000
 
zuul:
  host:
    connect-timeout-millis: 10000
    socket-timeout-millis: 60000
    #指定统一的前辍
  #prefix: /abc
  routes:
    # 指定路由规则
    abcmsc-consumer-depart: /abc012/**
    # /**:匹配任意多级路径   /abc8080/aaa/bbb/ccc
    # /* :仅匹配一级路径      /abc8080/aaa
    # /? :匹配仅包含一个字符的一级路径  /abc8080/a
 
  # 屏蔽指定微服务
  #ignored-services: abcmsc-consumer-depart-8090
  # 屏蔽所有微服务
  #ignored-services: "*"
 
  # 屏蔽指定URI
  # ignored-patterns: /**/list/**
 
  # 指定要屏蔽的敏感头信息
  #sensitive-headers: ["token","Set-Cookie"]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值