springcloud由入门到精通,超详细(一)

1.springcloud为什么这么火?现在实际开发为什么选择springcloud?它能解决什么样的问题?

1.1springcloud为什么这么火?

先回答第一个问题:互联网的发展离不开我们生活的进步,一切技术来源于生活,一切技术,服务于生活,早期互联网没有兴起的时候,单一应用足以满足用户的需求,2000年左右国内互联网刚刚兴起,由于用户量比较少,当时的开发并没有并发问题,开发人员不用考虑并发量的问题,因为单一应用足已解决用户较少的访问量,随着互联网的不发展,用户量不断地上升,单一应用不能满足市场的需求,一台服务器不足以支撑用户的访问量,当今互联网技术的迭代,也完全是为了解决高并发,提升用户体验的问题,为了不断的提升性能,互联网才不断的迭代更新。简单一句话,技术服务于生活。而springcloud能很好的处理,三高问题,高负载,高并发,高可用,问题,解决服务器压力的问题。

1.2 Java系统架构的演变(为了满足用户体验,高负载,高并发,高可用,三高问题,是互联网技术迭代的根本问题)

1.2.1 集中式开发

单一用用,集中式开发,一个应用,一台服务器,ORM映射,服务模块都在同一台服务器上,所有的业务都在同一台服务器上。
在这里插入图片描述

存在问题
不能解决用户的高并发问题,因为一台服务器,同一秒内最多访问的次数是有限的,例如一台服务器,同时能接收的线程数大约是200 左右
代码耦合性比价高:开发困难,维护成本比较高,
无法水平发展,业务臃肿
单点容错率地,并发能力差

1.2.2 垂直拆分

由于用户访问量比较大,单一应用无法满足用户的需求,此时为了更高的并发量,将不同的业务进行拆分,每个不通过的业务是一个不同的模块

在这里插入图片描述

优点:
对不同的模块进行功能的拆分,不同的功能写到不同的服务器上,解决了部分的并发问题
可以对不同的模块进行优化
容错率有提升,是一种负载均衡模式
缺点:
系统之间模块单独存放,代码大量重复,开发困难

1.2.3 分布式服务

业务的水平差分,每个模块之间的联系存在代码的冗余,将核心的功能抽取出来是非常必要的,渐渐形成稳定的服务中心,使服务更好的响应用户的需求,提高代码的复用性是非常有必要的
在这里插入图片描述
优点,提升了代码的复用性,提高了开发人员的开发效率。
缺点:系统之间的耦合性变高了,关系错乱,关系难以维护

1.2.4 服务治理架构

SOA架构,面向服务架构
当系统的功能越完善,系统的模块越多,此时增加调度中心,提高集群的利用率,此时服务调度,拥有一个实时的监管中心是关键,注册中心就是监管系统服务的,提高集群的利用率。
在这里插入图片描述
其中阿里巴巴的dubbo底层实现也是这一原理
服务越来越多。每个不同的地址都需要管理,服务之间的依赖关系难以维护,无法实时跟进服务的状态
服务治理解决的问题:
服务的注册中心:为每个服务自动注册,无需手动注册
服务的自动订阅,服务列表自动推送,服务调用的透明化,不用关系依赖关系。
动态的监管服务的状态,人为控制服务的装态。
缺点服务之间有依赖关系,一旦有问题出错,可能导致服务器宕机,
服务关系比较复杂

1.2.5微服务架构

微服务的优势:
单一职责
服务的粒度比较小
面向服务,支持result风格编程
自治:每个服务之间互不干扰
在这里插入图片描述

在这里插入图片描述

1.3 服务之间的调用RPC(dubbo)HTTP(springcloud)

常见的调用方式有两种,RPC HTTP调用
dubbo调用使用的使rpc调用,RPC调用时,基于原生的tcp协议,速度快,效率比较高。
springcloud是基于HTTP协议,http协议也是基于对应的tcp协议,http协议规定了请求的方式,携带的信息,自由灵活更符合微服务的理念。最重要的是支持result变成风格

2. 搭建基础项目服务

2.1搭建一个基础的maven项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
点击完成即可
在pom文件中引入相关的依赖


<packaging>pom</packaging>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR5</spring-cloud.version>
        <mysql.version>5.1.47</mysql.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--mybatis-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.1</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

在这里插入图片描述

2.2 分析需要的子工程

user-service服务提供者
consumer-service服务调用者

服务提供者的创建
创建子模块
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下一步点击finish即可

引入对应的依赖


<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>



在这里插入图片描述

下面为对应的包结构

在这里插入图片描述
在resources中编写配置文件
application.yaml文件


server:
  port: 8081
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
mybatis:
  type-aliases-package: com.sun.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level: 
  com.sun: debug

在这里插入图片描述
在这里插入图片描述
书写对应的包结构

编写对应的启动类

package com.sun;


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.sun.user.mapper")
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {

        SpringApplication.run(UserApplication.class, args);
    }
}

编写对应的mapper 文件,

package com.sun.user.mapper;

import com.sun.user.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {

    @Select("select * from user where id=#{id}")
    User findById(@Param("id") Long id);

}


编写对应的service

package com.sun.user.service;

import com.sun.user.mapper.UserMapper;
import com.sun.user.pojo.User;
import org.springframework.stereotype.Service;


@Service
public class UserService {

    private final UserMapper userMapper;


    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public User findById(Long id) {
        return userMapper.findById(id);
    }

}



编写对应的controller


package com.sun.user.controller;


import com.sun.user.pojo.User;
import com.sun.user.service.UserService;
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
@RequestMapping("/user")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public User findById(@PathVariable("id") Long id){

        return userService.findById(id);
    }
}


在浏览器展示运行结果:http://localhost:8081/user/1

在这里插入图片描述

创建服务调用者
创建工程与服务提供者一样
工程机构
在这里插入图片描述

引入依赖


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

在这里插入图片描述
创建服务调用者

编写yaml配置文件,指定端口
导入刚才的user类

在这里插入图片描述

创建启动类

package com.sun;


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 {

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

    public static void main(String[] args) {

        SpringApplication.run(ConsumerApplication.class, args);
    }
}


编写对应的调用方法


package com.sun.consumer.web;


import com.sun.consumer.pojo.User;
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;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/{id}")
    public User findById(@PathVariable("id") Long id) {
        String url = "http://localhost:8081/user/" + id;
        User user = restTemplate.getForObject(url, User.class);
        return user;
    }

}


运行效果展示
在这里插入图片描述
存在的问题
对外暴露的端口不宜修改
需要记忆对应的地址,不方便
只有一台服务器,不具备高可用的思想
服务者如果是集群,消费者需要自己实现负载均衡
springcloud可以解决这些问题

3.springcloud的核心模块介绍

Eureka提供注册中心:解决的问题,提供服务者,消费者,注册中心,消费者不用自己去找服务的提供者,直接从注册中心获取对应的服务
服务网关:gateway
Ribbon :提供负载均衡机制,提高系统资源的利用率
feign:服务调用更加方便,支持result编程风格
Hystrix:提供熔断器,服务隔离,服务降级,熔断机制

4.Eureka注册中心

注册中心,提供三组,分别是注册中心,服务提供者,服务消费者
简述三者的关系:
正所谓所有的技术都来源于生活,注册中心就类似于现实生活中的租房问题,注册中心就扮演生活中的中介公司,甲方房东就类似于服务提供者,为租客提供房源,租客类似于现实生活中的租客,房东想出租房屋,需要找中介公司代理,告诉中介公司,我这有房子要出租,中介公司进行挂载,如果有对应的租客找到中介公司,表述自己需要的房子类型,中介公司寻找匹配的房源,给与租客,这样就省去了租客不断的寻找房东的问题了,租客不需要记住房东的电话,只需记住对应的中介共公司就行了,省去了中间繁琐的过程。

在这里插入图片描述
Eureka-server就是注册中心,对外暴露对应的地址
服务提供者:启动后向注册中心注册自己的信息,并且定期续约
服务消费者:服务调用的一方,定期向Eureka拉去对应的服务列表,然后通过负载均衡定期向服务进行调用。
心跳续约:服务提供者定期向注册中心发送请求,刷新自己的状态

4.1.案例练习

创建一个eureka-service

创建i项目:
在这里插入图片描述
导入对应的依赖:

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


编写对应的启动类


package com.sun;


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

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

}


编写对应的application.yaml配置文件


server:
  port: 8088
spring:
  application:
    name: eureka-service
eureka:
  client:
    service-url: # eureka的地址,现在是自己的地址
      defaultZone: http://localhost:8088/eureka
    register-with-eureka: false  # 是否注册自己
    fetch-registry: false # 是否拉去服务
    
    
    
    

访问对应的服务地址,就可以看到对应的展示信息

在这里插入图片描述

修改服务提供者
在对应的pom文件中引入对应的jar包依赖


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

修改对应的application.yaml服务提供者user


server:
  port: 8081
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
  application:
    name: user-service

mybatis:
  type-aliases-package: com.sun.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    com.sun: debug
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8088/eureka


重启对应的user-service可以看到对应的eureka-service中添加了一个注册信息
在这里插入图片描述

服务的消费者

在对应的pom文件中获取对应的jar包依赖


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


修改对应的consumer-service的pom文件


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


修改对应的consumer-service中的controller的方法

package com.sun.consumer.web;


import com.sun.consumer.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.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;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("/{id}")
    public User findById(@PathVariable("id") Long id) {
        //String url = "http://localhost:8081/user/" + id;
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");

        ServiceInstance instance = instances.get(0);

        String url = String.format("http://%s:%s/user/%s", instance.getHost(), instance.getPort(), id);

        User user = restTemplate.getForObject(url, User.class);
        return user;
    }

}


重启服务器
运行对应的结果

可以看出eureka中拥有两个请求
在这里插入图片描述
访问对应的consumer服务,剋以很明显的看出,显示效果也是没有问题的

在这里插入图片描述

4.2 eureka的详细介绍

系统分为三个基础架构
服务注册中心:eureka的服务注册应用为系统服务提供和发现,

服务提供者:提供服务应用,只要是对应位系统服务的rest风格即可,刚才的user-service
服务消费者:从注册中心获取对应的服务,例如对应的consumer

eureka’支持高可用,可以是一台服务器,也可以是一个集群
如果是一个集群,eureka的信息也会相互调用,完成对应的功能实现,从而实现高可用的效果,无论eureka-service集群中的任何一个节点,都可以获得完整的服务列表信息。

在这里插入图片描述
eureka之间相互注册,可以让服务提供者,服务消费者,出现资源不匹配的问题,不同服务器之间,服务提供供者存在,而服务消费者拉去不到对应的服务

4.3 搭建eureka集群

搭建集群只需要注册对应的eureka服务即可
三台服务器相互注册
方便展示,这里使用两台,集群的数量根据公司用户量来定
这样就能展示对应的eureka服务器的相关信息
正常流程是需要再创建一台eureka服务器,这里方便演示直接复制了
在这里插入图片描述

指定对应的端口号
释放自注册在配置文件中
在这里插入图片描述

在这里插入图片描述

这时我们可以让consumer在端口为8089的eureka服务器中获取对应的信息,只需修改端口就好在这里插入图片描述

看到展示结果也是没有问题的
在这里插入图片描述

4.4其他的相关的配置

eurek注册相关的主机是优先提供对应的配置信息,展示的是电脑的主机名,而不是服务的ip,如果想优先展示主机名,就可以使用下面相关配置application.yaml
在这里插入图片描述


eureka:
  client:
    service-url:
      defaultZone: http://localhost:8088/eureka
  instance:
    ip-address: 127.0.0.1
    prefer-ip-address: true
    instance-id: ${eureka.instance.ip-address}:${server.port} # 自定义实例的id


服务续约:
一般采用默认即可:
类似于服务的提供者向注册中心说,我还活着,
eureka:
instance:
lease-expiration-duration-in-seconds: 90 #服务续约的时间间隔
lease-renewal-interval-in-seconds: 30 #服务续约的失效时间,默认为90s

当服务下线,或者服务失效,eureka会将该服务剔除,自我保护模式是more打开的,如果想要关闭可以使用一下配置
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)

总结:
在eureka中,服务的注册和发现都是可以控制的,可以是关闭的,也可以是打开的
注册中心需要服务提供者的心跳,即心跳续约,如果没有,则超过90s默认为宕机
服务默认为30s拉去一次
eureka会默认60s剔除宕机的服务。
eureka有自我保护机制,当心跳比例超过对应的阈值,那么开启对应的自我保护机制
eureka是高可用的

5.负载均衡Ribbon

在这里插入图片描述
多个请求,怎么使服务器分配到不同的服务器处理不同的业务呢,,负载均衡就是解决这一问题的,利用负载均衡算法,分配到不同的服务器上
使用上面的案例复制一个user-service请求
在这里插入图片描述
启动两台不同的user-service服务器
只需要在启动类上加入对应的负载均衡注解即可
在这里插入图片描述
@LoadBalanced加载消费者一端这里是consumer

修改对应的controller完成对端口的调用,不然会报异常

package com.sun.consumer.web;


import com.sun.consumer.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.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;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("/{id}")
    public User findById(@PathVariable("id") Long id) {
        //String url = "http://localhost:8081/user/" + id;
        String url = "http://user-service/user/" + id;
       // List<ServiceInstance> instances = discoveryClient.getInstances("user-service");

        //ServiceInstance instance = instances.get(0);

       // String url = String.format("http://%s:%s/user/%s", instance.getHost(), instance.getPort(), id);

        User user = restTemplate.getForObject(url, User.class);
        return user;
    }

}


重启服务器发送请求

在这里插入图片描述

思路分析:
在这里插入图片描述

思路表述:
1.用户向服务器发送请求
2.consumer根据id对应的请求,向eureka列表拉取请求
3.eureka返回对应的请求信息,consumer请求第二次就无需拉去了,
4.第二次拉去是ribbon负载均衡策略,从中选择一个。
5.向选中的服务发送请求

注意事项:ribbon默认的是懒加载,往往第一次请求会出现ribbon:
eager-load:
enabled: true
clients: user-service超时现象,如果使用积极加载需要配置一下内容


版权声明:本文为qq_41125149原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。