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(代码片段
[菜鸟SpringCloud入门]第一章:构建多模块的Maven项目+创建注册中心Eureka子模块