springCloud入门

Posted G_whang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springCloud入门相关的知识,希望对你有一定的参考价值。

服务注册与服务发现
上节讲到,用户预约图书馆的时候,使用了restTemplate调用图书馆服务,方法如下:
在这里插入图片描述
可以看到调用的地址是写死的,这样维护成本非常高,稍微变个端口或者ip就需要在代码里面重新维护,服务少还能接收,如果有100个,或者200个那只单单改IP和端口就需要好长时间。
所以我们要思考一下,怎样才能让服务消费者总能找到服务提供者呢?

目前市面上把服务消费者找到服务提供者的这种机制称为服务发现,又或者服务注册

服务发现原理很简单
好比我们在mysql中建立一张注册表

  • 应用启动时,自动往注册表中插入一条数据,数据包括服务名称、IP、端口等信息。
  • 应用停止时,自动把自己在注册表中的数据的状态设为 DOWN 。

这样,服务消费者就能找到服务提供者了。当服务消费者想调用服务提供者接口时,只需向数据库发送SQL语句 SELECT * FROM registry where service_name=‘user’ and status=‘UP’ 即可找到服务提供者的所有实例!IP、端口啥的都有了,自己拼接一下,再去调用就行了!这样就实现了一个非常简单的注册中心了,因为简单所以会存在诸多问题,
比如:数据库突然宕机,无法提供服务时候,应该把注册表中的数据状态改为DOWN,因为数据库都挂了,更别提修改了;服务每次调用都来查一次数据库,请求过多会影响服务器性能;
现在我们就发现服务如果想要健壮还缺少非常重要的机制,那就是检测机制,和缓存机制
检测机制就是服务发现组件如长时间无法与某微服务实例通信,就会自动注销该实例
缓存机制就是各个微服务将需要调用服务的地址缓存在本地,并使用一定机制更新这样既能降低服务发现组件的压力,同时,即使服务发现组件出问题,也不会影响到服务之间的调用。

其实springcloud也提供了一个非常好用的注册组件 Eureka
Eureka核心组件:Eureka Server 和 Eureka Client,它们的作用如下:

  • Eureka Server提供服务发现的能力,各个微服务启动时,会向Eureka
    Server注册自己的信息(例如IP、端口、微服务名称等),Eureka Server会存储这些信息;
  • Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;
  • 微服务启动后,会周期性(默认30秒)地向Eureka Server发送心跳以续约自己的“租期”;
  • 如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒;
  • 默认情况下,Eureka Server同时也是Eureka Client。多个Eureka Server实例,互相之间通过增量复制的方式,来实现服务注册表中数据的同步。Eureka Server默认保证在90秒内,Eureka Server集群内的所有实例中的数据达到一致(从这个架构来看,Eureka Server所有实例所处的角色都是对等的,没有类似Zookeeper、Consul、Etcd等软件的选举过程,也不存在主从,所有的节点都是主节点。Eureka官方将Eureka Server集群中的所有实例称为“对等体(peer)”)
  • Eureka Client会缓存服务注册表中的信息。这种方式有一定的优势——首先,微服务无需每次请求都查询Eureka Server,从而降低了Eureka Server的压力;其次,即使Eureka Server所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用。

Eureka通过心跳检查、客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。

代码如下:
Eureka Server 注册中心
maven 如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>eurekaServer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eurekaServer</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--增加eurekaServer-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

启动类如下:


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

@SpringBootApplication
// 服务发现
@EnableEurekaServer
public class EurekaServerApplication {

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

}

配置文件

server:
  port: 8761
eureka:
  client:
    # 是否要注册到其他eureka server 实例
    register-with-eureka: false
    # 是否从其他eureka server实例获取数据
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8761/eureka/

服务提供者代码如下:
maven文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>test-provider-library</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>test-provider-library</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- 引入H2数据库,一种内嵌的数据库,语法类似MySQL -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <!-- 引入Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--增加eurekaclient-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

启动类如下


import com.example.testproviderlibrary.dao.LibraryDao;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

import java.util.stream.Stream;

@SpringBootApplication
// 加注解
@EnableEurekaClient
public class TestProviderLibraryApplication {

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

    /**
     * 使用内置数据库h2 初始化数据
     */
    @Bean
    ApplicationRunner init (LibraryDao repository){
        return args ->{
            LibraryInfo libraryInfo1=LibraryInfo.builder().id(1L).name("前门图书馆")
                    .address("东城区前门东大街").number(1000).count(20).build();
            LibraryInfo libraryInfo2=LibraryInfo.builder().id(2L).name("青年路图书馆")
                    .address("丰台区青年路").number(500).count(10).build();
            LibraryInfo libraryInfo3=LibraryInfo.builder().id(2L).name("大地图书馆")
                    .address("昌平区上地八街").number(800).count(2).build();
            Stream.of(libraryInfo1,libraryInfo2,libraryInfo3).forEach(repository :: save);


        };
    }

}

controller


import com.example.testproviderlibrary.pojo.LibraryInfo;
import com.example.testproviderlibrary.service.LibraryService;
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 java.util.Optional;

@RestController
@RequestMapping("/library")
public class LibraryController {

    @Autowired
    private LibraryService libraryService;

    @GetMapping("/{id}")
    public LibraryInfo findById(@PathVariable Long id){
        Optional<LibraryInfo> libraryInfo = this.libraryService.findById(id);
        return libraryInfo.get();
    }

}

dao


import com.example.testproviderlibrary.pojo.LibraryInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface LibraryDao extends JpaRepository<LibraryInfo,Long> {
}

service

import com.example.testproviderlibrary.dao.LibraryDao;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
//可以代替@AutoWired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解
@RequiredArgsConstructor
public class LibraryService {


    private final LibraryDao libraryDao;

    public Optional<LibraryInfo> findById(Long id){
        return this.libraryDao.findById(id);
    }


}

实体类

import lombok.*;

import javax.persistence.*;

/**
 * 图书馆信息
 *
 */
@Getter
@Setter
@Builder
@Entity
// 为类提供一个无参的构造方法。
@NoArgsConstructor
// 为类提供一个全参的构造方法
@AllArgsConstructor
public class LibraryInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    /**
     * 图书馆名称
     */
    @Column
    private String name;

    /**
     * 图书馆地址
     */
    @Column
    private String address;

    /**
     * 总座位数
     */
    @Column
    private Integer number;

    /**
     * 可预约数量
     */
    @Column
    private Integer count;

}

配置文件

server:
  # 指定Tomcat端口
  port: 8000
spring:
  jpa:
  # 让hibernate打印执行的SQL
     how-sql: true
    ## 指定注册到eureka server上的服务名称
  application:
    name: test-provider-library
logging:
  level:
    root: INFO
    # 配置日志级别,让hibernate打印出执行的SQL参数
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://localhost:8761/eureka/
  instance:
    # 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
    prefer-ip-address: true


服务消费者
maven引入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>test-consumer-user-eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>test-consumer-user-eureka</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 引入Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--增加eurekaclient-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class TestConsumerUserEurekaApplication {

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

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

    }

}

实体类

import lombok.*;

/**
 * 图书馆信息
 *
 */
@Getter
@Setter
@Builder
@NoArgsConstructor
// 为类提供一个全参的构造方法
@AllArgsConstructor
public class LibraryInfo {


    private Long id;

    /**
     * 图书馆名称
     */
    private String name;

    /**
     * 图书馆地址
     */
    private String address;

    /**
     * 总座位数
     */
    private Integer number;

    /**
     * 可预约数量
     */
    private Integer count;

}

controller


import com.example.testconsumerusereureka.pojo.LibraryInfo;
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("/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/library/{id}")
    public LibraryInfo findById(@PathVariable Long id){
        // 这里用到了RestTemplate的占位符能力
        LibraryInfo forObject = restTemplate.getForObject("http://test-provider-library/library/{id}",
                LibraryInfo.class, id);
        return forObject;
    }

}

配置文件

server:
  port: 8002
spring:
    ## 指定注册到eureka server上的服务名称
  application:
    name: test-consumer-user-eureka
eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://localhost:8761/eureka/
  instance:
    # 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
    prefer-ip-address: true

依次启动
eureka_server
test-provider-library
test-consumer-user-eureka
然后先访问注册中心

http://localhost:8761

在这里插入图片描述
可以发现服务已经注册成功了
然后调用

http://localhost:8002/user/library/1

在这里插入图片描述

以上是关于springCloud入门的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端Eureka 服务信息Eureka 发现管理Eureka 安全配置Eureka-HA(高可用) 机制Eur(代码片段

推荐net开发cad入门阅读代码片段

SpringCloud入门[转]

[菜鸟SpringCloud入门]第一章:构建多模块的Maven项目+创建注册中心Eureka子模块

[菜鸟SpringCloud实战入门]第八章:通过消息总线Bus实现配置文件统一刷新(使用Kafka)

Java 微服务之 SpringCloud快速入门day02 Feign