typora-copy-images-to |
---|
img |
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。 Eureka Server本身也是一个服务,默认情况下会自动注册到Eureka注册中心。 如果搭建单机版的Eureka Server注册中心,则需要配置取消Eureka Server的自动注册逻辑。毕竟当前服务注册到当前服务代表的注册中心中是一个说不通的逻辑。 Eureka Server通过Register、Get、Renew等接口提供服务的注册、发现和心跳检测等服务。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。 Eureka Client分为两个角色,
分别是:Application Service(Service Provider)和Application Client(Service Consumer)
服务提供方,是注册到Eureka Server中的服务。
服务消费方,通过Eureka Server发现服务,并消费。
在这里,Application Service和Application Client不是绝对上的定义,因为Provider在提供服务的同时,也可以消费其他Provider提供的服务;Consumer在消费服务的同时,也可以提供对外服务。
Register(服务注册):把自己的IP和端口注册给Eureka。
Renew(服务续约):发送心跳包,每30秒发送一次。告诉Eureka自己还活着。
Cancel(服务下线):当provider关闭时会向Eureka发送消息,把自己从服务列表中删除。防止consumer调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):eureka集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。
简单介绍下cap原理
CAP定理:CAP三个属性对于分布式系统不同同时做到。如AP/CP/AC。再来看Zookeepr区别:
(1)Zookeeper是CP,分布式协同服务,突出一致性。对ZooKeeper的的每次请求都能得到一致的数据结果,但是无法保证每次访问服务可用性。如请求到来时,zookeer正在做leader选举,此时不能提供服务,即不满足A可用性。
(2)Euere是AP,高可用与可伸缩的Service发现服务,突出可用性。相对于Zookeeper而言,可能返回数据没有一致性,但是保证能够返回数据,服务是可用的。
分为single node和HA两种方式。
<?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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kgc.tangccp</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>eureka-server</name>
<description>single eureka server project for Spring Boot</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
server:
port: 8761
servlet:
encoding:
charset: UTF-8
force: true
spring:
application:
name: eureka-server
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
client:
register-with-eureka: false # 是否将自己注册到注册中心 默认值是 true
fetch-registry: false # 是否从注册中心获取服务注册信息 默认值是 true
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
通过@EnableEurekaSwerver来标识该服务为Eureka Server。
package cn.kgc.tangcco;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
<?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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kgc.tangcco</groupId>
<artifactId>eureka-server01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>eureka-server01</name>
<description>eureka server ha project for Spring Boot</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
<?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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kgc.tangcco</groupId>
<artifactId>eureka-server02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>eureka-server02</name>
<description>eureka server ha project for Spring Boot</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
注意:
eureka.client.serviceUrl.defaultZone=其它节点地址
多个节点间使用逗号间隔
server:
port: 8761
servlet:
encoding:
charset: UTF-8
force: true
spring:
application:
name: eureka-server #应用名称 集群环境下相同
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url:
defaultZone: http://localhost:8762/eureka/
server:
enable-self-preservation: true # 关闭注册中心自我保护机制
eviction-interval-timer-in-ms: 10000 # 注册中心清理间隔,单位为毫秒
logging:
level:
com.netflix: warn
server:
port: 8762
servlet:
encoding:
charset: UTF-8
force: true
spring:
application:
name: eureka-server #应用名称 集群环境下相同
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url:
defaultZone: http://localhost:8761/eureka/
server:
enable-self-preservation: true # 关闭注册中心自我保护机制
eviction-interval-timer-in-ms: 10000 # 注册中心清理间隔,单位为毫秒
logging:
level:
com.netflix: warn
通过@EnableEurekaSwerver来标识该服务为Eureka Server。
当我们不配置Eureka账号密码是,只要输入地址即可访问,下面记录如何对Eureka的服务中心进行加密
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
server:
port: 8761
servlet:
encoding:
charset: UTF-8
force: true
spring:
application:
name: eureka-server #应用名称 集群环境下相同
security:
user:
name: admin
password: 123456
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url:
defaultZone: http://admin:123456@localhost:8762/eureka/
server:
enable-self-preservation: true # 关闭注册中心自我保护机制
eviction-interval-timer-in-ms: 10000 # 注册中心清理间隔,单位为毫秒
logging:
level:
com.netflix: warn
server:
port: 8762
servlet:
encoding:
charset: UTF-8
force: true
spring:
application:
name: eureka-server #应用名称 集群环境下相同
security:
user:
name: admin
password: 123456
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url:
defaultZone: http://admin:123456@localhost:8761/eureka/
server:
enable-self-preservation: true # 关闭注册中心自我保护机制
eviction-interval-timer-in-ms: 10000 # 注册中心清理间隔,单位为毫秒
logging:
level:
com.netflix: warn
package cn.kgc.tangcco.configurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author 李昊哲
* @Description
* @create 2021/1/27 16:07
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// /eureka路径忽略csrf
http.csrf().ignoringAntMatchers("/**");
super.configure(http);
}
}
登录后页面
登录后页面
包括两个服务模块:Service Provider(服务提供方)和Service Consumer(服务消费方)。
<?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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kgc.tangcco</groupId>
<artifactId>idiom-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>idiom-provider</name>
<description>idiom-provider project for Spring Boot</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>${project.artifactId}</finalName>
<outputDirectory>../package</outputDirectory>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
server:
port: 8080
servlet:
encoding:
force: true
charset: UTF-8
spring:
application:
name: idiom-provider #应用名称 集群环境下相同
servlet:
multipart:
max-file-size: 50MB # 文件上传大小限制为500kb
max-request-size: 200MB # 请求大小限制为500kb
datasource:
url: jdbc:mysql://47.94.130.233:3306/knowledge?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimeZone=Aisa/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 200
# 配置获取连接等待超时的时间
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
max-pool-prepared-statement-per-connection-size: 20
filters: stat,wall,slf4j
use-global-data-source-stat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
stat-view-servlet:
login-username: admin
login-password: 123456
reset-enable: false
url-pattern: /druid/*
allow: 0.0.0.0
#deny:
enabled: true
web-stat-filter:
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
jpa:
database: MySQL
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: none
eureka:
instance:
# hostname: localhost # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url: # 设置注册中心地址
defaultZone: http://admin:123456@192.168.1.105:8761/eureka/,http://admin:123456@192.168.1.105:8762/eureka/
# 度量指标监控与健康检查
management:
endpoints:
web:
exposure:
include: shutdown # 开启 shutdown 端点访问
endpoint:
shutdown:
enabled: true # 开启 shutdown 请求方式必须是post
logging:
level:
com.netflix: warn
package cn.kgc.tangcco;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class IdiomProviderApplication {
public static void main(String[] args) {
SpringApplication.run(IdiomProviderApplication.class, args);
}
}
package cn.kgc.tangcco.pojo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author 李昊哲
* @Description
* @create 2020/12/29 14:22
*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Table(name = "idiom")
@Entity
public class Idiom implements Serializable {
private static final long serialVersionUID = 7553935244407585482L;
/**
* 主键ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
/**
* 成语
*/
@Column(name = "name")
private String name;
/**
* 拼音
*/
@Column(name = "spell")
private String spell;
/**
* 解释
*/
@Column(name = "content")
private String content;
/**
* 典故
*/
@Column(name = "derivation")
private String derivation;
/**
* 举例句子
*/
@Column(name = "samples")
private String samples;
}
package cn.kgc.tangcco.repository;
import cn.kgc.tangcco.pojo.Idiom;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
/**
* @author 李昊哲
* @Description
* @create 2020/12/29 14:48
*/
@Repository
public interface IdiomRepository extends JpaRepository<Idiom,Integer>, JpaSpecificationExecutor<Idiom> {}
package cn.kgc.tangcco.service;
import cn.kgc.tangcco.pojo.Idiom;
import org.springframework.data.domain.Page;
import java.util.List;
/**
* @author 李昊哲
* @Description
* @create 2020/12/29 14:51
*/
public interface IdiomService {
/**
* 分页查询
*
* @param page 查询页码
* @param limit 每页记录数
* @return
*/
public Page<Idiom> finAll(Integer page, Integer limit);
}
package cn.kgc.tangcco.service.impl;
import cn.kgc.tangcco.repository.IdiomRepository;
import cn.kgc.tangcco.pojo.Idiom;
import cn.kgc.tangcco.service.IdiomService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
/**
* @author 李昊哲
* @Description
* @create 2021/1/27 11:51
*/
@Service
public class IdiomServiceImpl implements IdiomService {
private final IdiomRepository idiomRepository;
public IdiomServiceImpl(IdiomRepository idiomRepository) {
this.idiomRepository = idiomRepository;
}
@Override
public Page<Idiom> finAll(Integer page, Integer limit) {
return idiomRepository.findAll(PageRequest.of(page - 1, limit));
}
}
package cn.kgc.tangcco.controller;
import cn.kgc.tangcco.pojo.Idiom;
import cn.kgc.tangcco.service.IdiomService;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 李昊哲
* @Description
* @create 2021/1/27 11:59
*/
@RestController
@RequestMapping(value = "/idiom")
public class IdiomController {
private final IdiomService idiomService;
public IdiomController(IdiomService idiomService) {
this.idiomService = idiomService;
}
@PostMapping(value = "/page")
public Page<Idiom> page(Integer page, Integer limit){
return idiomService.finAll(page,limit);
}
}
测试地址:http://localhost:8080/idiom/page
请求方式:POST
请求参数:
key | value |
---|---|
page | 2 |
limit | 2 |
返回结果:
{
"content": [
{
"id": 3,
"name": "备而不用",
"spell": "bèi ér bù yòng",
"content": "准备好了,以备急用,眼下暂存不用。",
"derivation": "清·吴趼人《糊涂世界》:“虽说备而不用,到得那时候,听凭兵丁造一句谣言,开上几排枪,那人可就死了不少。”",
"samples": "凡是零星物件,本地买不出,一定要用,或是~的,也都齐全。(清·颐琐《黄绣球》第十七回)"
},
{
"id": 4,
"name": "贝阙珠宫",
"spell": "bèi què zhū gōng",
"content": "用珍珠宝贝做的宫殿。形容房屋华丽。",
"derivation": "战国·楚·屈原《九歌·河伯》:“鱼鳞屋兮龙堂,紫贝阙兮朱宫。”",
"samples": "你看那香焚宝鼎,紫雾漾漾,玉楼金殿,~,便如天宫之景也。(明·无名氏《庆长生》第四折)"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 2,
"pageSize": 2,
"pageNumber": 1,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 15926,
"totalElements": 31851,
"size": 2,
"number": 1,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": false,
"numberOfElements": 2,
"empty": false
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: shutdown # 开启 shutdown 端点访问
endpoint:
shutdown:
enabled: true # 开启 shutdown 请求方式必须是post
请求地址:http://localhost:8080/actuator/shutdown
请求方式:POST
返回结果:
{
"message": "Shutting down, bye..."
}
停服后从注册中心剔除服务
重新启动服务
<?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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kgc.tangcco</groupId>
<artifactId>idiom-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>idiom-consumer</name>
<description>idiom consumer project for Spring Boot</description>
<properties>
<java.version>15</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
server:
port: 8080
servlet:
encoding:
force: true
charset: UTF-8
spring:
application:
name: idiom-consumer #应用名称 集群环境下相同
servlet:
multipart:
max-file-size: 50MB # 文件上传大小限制为500kb
max-request-size: 200MB # 请求大小限制为500kb
eureka:
instance:
hostname: phoenix # 主机名 默认使用操作系统主机名
preferIpAddress: true # 是否使用 IP 地址注册
instanceId: ${spring.cloud.client.ip-address}:${server.port}
client:
egistry-fetch-interval-seconds: 10 # Eureka Client 间隔多久去服务器拉取注册信息 默认是30秒
register-with-eureka: false # 是否将自己注册到注册中心 默认值是 true
fetch-registry: true # 是否从注册中心获取服务注册信息 默认值是 true
service-url: # 设置注册中心地址
defaultZone: http://admin:123456@192.168.1.105:8761/eureka/,http://admin:123456@192.168.1.105:8762/eureka/
logging:
level:
com.netflix: warn
package cn.kgc.tangcco;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestOperations;
@EnableDiscoveryClient
@SpringBootApplication
public class IdiomConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(IdiomConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestOperations restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
package cn.kgc.tangcco.pojo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @author 李昊哲
* @Description
* @create 2020/12/29 14:22
*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Idiom implements Serializable {
private static final long serialVersionUID = 7553935244407585482L;
/**
* 主键ID
*/
private Integer id;
/**
* 成语
*/
private String name;
/**
* 拼音
*/
private String spell;
/**
* 解释
*/
private String content;
/**
* 典故
*/
private String derivation;
/**
* 举例句子
*/
private String samples;
}
package cn.kgc.tangcco.service;
import cn.kgc.tangcco.pojo.Idiom;
/**
* @author 李昊哲
* @Description
* @create 2020/12/29 14:51
*/
public interface IdiomService {
/**
* ByDiscoveryClient 分页查询
*
* @param page 查询页码
* @param limit 每页记录数
* @return
*/
public String finAllByDiscoveryClient(Integer page, Integer limit);
/**
* LoadBalancerClient 分页查询
*
* @param page 查询页码
* @param limit 每页记录数
* @return
*/
public String finAllByLoadBalancerClient(Integer page, Integer limit);
/**
* LoadBalancerClient 分页查询
*
* @param page 查询页码
* @param limit 每页记录数
* @return
*/
public String finAllByLoadBalancerClientAnnotation(Integer page, Integer limit);
}
package cn.kgc.tangcco.service.impl;
import cn.kgc.tangcco.service.IdiomService;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestOperations;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author 李昊哲
* @Description
* @create 2021/1/27 18:50
*/
@Service
public class IdiomServiceImpl implements IdiomService {
private final DiscoveryClient discoveryClient;
// Ribben 负载均衡器
private final LoadBalancerClient loadBalancerClient;
private final RestOperations restTemplate;
public IdiomServiceImpl(DiscoveryClient discoveryClient, LoadBalancerClient loadBalancerClient, RestOperations restTemplate) {
this.discoveryClient = discoveryClient;
this.loadBalancerClient = loadBalancerClient;
this.restTemplate = restTemplate;
}
@Override
public String finAllByDiscoveryClient(Integer page, Integer limit) {
List<String> services = discoveryClient.getServices();
if (CollectionUtils.isEmpty(services)) {
// 未获取服务列表 则返回null
return null;
}
// 根据服务名称获取服务
List<ServiceInstance> instances = discoveryClient.getInstances("idiom-provider");
if (CollectionUtils.isEmpty(instances)) {
// 未获取服务 则返回null
return null;
}
ServiceInstance serviceInstance = instances.get(ThreadLocalRandom.current().nextInt(instances.size()));
StringBuffer sb = new StringBuffer();
sb.append("http://");
sb.append(serviceInstance.getHost());
sb.append(":");
sb.append(serviceInstance.getPort());
sb.append("/idiom/page");
String url = sb.toString();
HttpHeaders headers = new HttpHeaders();
// 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
MultiValueMap<String, Integer> params = new LinkedMultiValueMap<>();
// 也支持中文
params.add("page", page);
params.add("limit", limit);
HttpEntity<MultiValueMap<String, Integer>> requestEntity = new HttpEntity<>(params, headers);
// 执行HTTP请求
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, new ParameterizedTypeReference<>() {
});
// 输出结果
return response.getBody();
}
@Override
public String finAllByLoadBalancerClient(Integer page, Integer limit) {
// 根据服务名称获取服务
ServiceInstance serviceInstance = loadBalancerClient.choose("idiom-provider");
if (serviceInstance == null) {
return null;
}
StringBuffer sb = new StringBuffer();
sb.append("http://");
sb.append(serviceInstance.getHost());
sb.append(":");
sb.append(serviceInstance.getPort());
sb.append("/idiom/page");
String url = sb.toString();
HttpHeaders headers = new HttpHeaders();
// 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
MultiValueMap<String, Integer> params = new LinkedMultiValueMap<>();
// 也支持中文
params.add("page", page);
params.add("limit", limit);
HttpEntity<MultiValueMap<String, Integer>> requestEntity = new HttpEntity<>(params, headers);
// 执行HTTP请求
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, new ParameterizedTypeReference<>() {
});
// 输出结果
return response.getBody();
}
@Override
public String finAllByLoadBalancerClientAnnotation(Integer page, Integer limit) {
String url = "http://idiom-provider/idiom/page";
HttpHeaders headers = new HttpHeaders();
// 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
MultiValueMap<String, Integer> params = new LinkedMultiValueMap<>();
// 也支持中文
params.add("page", page);
params.add("limit", limit);
HttpEntity<MultiValueMap<String, Integer>> requestEntity = new HttpEntity<>(params, headers);
// 执行HTTP请求
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, new ParameterizedTypeReference<>() {
});
// 输出结果
return response.getBody();
}
}
package cn.kgc.tangcco.controller;
import cn.kgc.tangcco.service.IdiomService;
import org.springframework.web.bind.annotation.*;
/**
* @author 李昊哲
* @Description
* @create 2021/1/27 11:59
*/
@RestController
@RequestMapping(value = "/idiom")
public class IdiomController {
private final IdiomService idiomService;
public IdiomController(IdiomService idiomService) {
this.idiomService = idiomService;
}
@GetMapping(value = "/finAllByDiscoveryClient")
public String finAllByDiscoveryClient(
@RequestParam(name = "page",defaultValue = "1",required = true) Integer page,
@RequestParam(name = "limit",defaultValue = "2",required = true) Integer limit){
return idiomService.finAllByDiscoveryClient(page,limit);
}
@GetMapping(value = "/finAllByLoadBalancerClient")
public String finAllByLoadBalancerClient(
@RequestParam(name = "page",defaultValue = "1",required = true) Integer page,
@RequestParam(name = "limit",defaultValue = "2",required = true) Integer limit){
return idiomService.finAllByDiscoveryClient(page,limit);
}
@GetMapping(value = "/finAllByLoadBalancerClientAnnotation")
public String finAllByLoadBalancerClientAnnotation(
@RequestParam(name = "page",defaultValue = "1",required = true) Integer page,
@RequestParam(name = "limit",defaultValue = "2",required = true) Integer limit){
return idiomService.finAllByLoadBalancerClientAnnotation(page,limit);
}
}
java -jar idiom-provider.war --server.port=10001
java -jar idiom-provider.war --server.port=10002
请求地址:http://localhost:8080/idiom/finAllByDiscoveryClient
请求方式:GET
请求参数:
key | value |
---|---|
page | 2 |
limit | 2 |
返回结果:
{
"content": [
{
"id": 3,
"name": "备而不用",
"spell": "bèi ér bù yòng",
"content": "准备好了,以备急用,眼下暂存不用。",
"derivation": "清·吴趼人《糊涂世界》:“虽说备而不用,到得那时候,听凭兵丁造一句谣言,开上几排枪,那人可就死了不少。”",
"samples": "凡是零星物件,本地买不出,一定要用,或是~的,也都齐全。(清·颐琐《黄绣球》第十七回)"
},
{
"id": 4,
"name": "贝阙珠宫",
"spell": "bèi què zhū gōng",
"content": "用珍珠宝贝做的宫殿。形容房屋华丽。",
"derivation": "战国·楚·屈原《九歌·河伯》:“鱼鳞屋兮龙堂,紫贝阙兮朱宫。”",
"samples": "你看那香焚宝鼎,紫雾漾漾,玉楼金殿,~,便如天宫之景也。(明·无名氏《庆长生》第四折)"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 2,
"pageSize": 2,
"pageNumber": 1,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 15926,
"totalElements": 31851,
"size": 2,
"number": 1,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": false,
"numberOfElements": 2,
"empty": false
}
请求地址:http://localhost:8080/idiom/finAllByLoadBalancerClient
请求方式:GET
请求参数:
key | value |
---|---|
page | 4 |
limit | 2 |
返回结果:
{
"content": [
{
"id": 7,
"name": "毕恭毕敬",
"spell": "bì gōng bì jìng",
"content": "形容态度十分恭敬。",
"derivation": "《诗经·小雅·小弁》:“维桑与梓,必恭敬止,靡瞻匪父,靡依匪母。”",
"samples": "他~地将书递给了老师,请求解答两个疑难问题。"
},
{
"id": 8,
"name": "婢作夫人",
"spell": "bì zuò fū rén",
"content": "婢:侍女;夫人:主妇。旧时指在文艺方面虽刻意模仿别人,但才力和作品的规模总赶不上。",
"derivation": "唐·张彦远《法书要录》引南朝梁袁昂《古今书评》:“羊欣书如大家婢为夫人。虽处其位,而举止羞涩,终不似真。”",
"samples": "不过偶尔学着写,正是~,那里及得到大哥什一。(清·吴趼人《二十年目睹之怪现状》第四十一回)"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 6,
"pageNumber": 3,
"pageSize": 2,
"paged": true,
"unpaged": false
},
"totalPages": 15926,
"totalElements": 31851,
"last": false,
"size": 2,
"number": 3,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"numberOfElements": 2,
"first": false,
"empty": false
}
请求地址:http://localhost:8080/idiom/finAllByLoadBalancerClientAnnotation
请求方式:GET
请求参数:
key | value |
---|---|
page | 5 |
limit | 2 |
返回结果:
{
"content": [
{
"id": 9,
"name": "壁垒森严",
"spell": "bì lěi sēn yán",
"content": "壁垒:古代军营四周的围墙;森严:整齐,严肃。原指军事戒备严密。现也用来比喻彼此界限划得很分明。",
"derivation": null,
"samples": "声威震摄长愉塞,~细柳营。(刘复《代某君寿某将军夫妇》)"
},
{
"id": 10,
"name": "鞭辟近里",
"spell": "biān pì jìn lǐ",
"content": "鞭辟:鞭策,激励;里:最里层。形容作学问切实。也形容分析透彻,切中要害。",
"derivation": "《二程全书·遗书十一》:“学只要鞭辟近里,著己而已。”",
"samples": "~一言,实吾人顶门针、对症药。(清·李颙《答顾宁人书》)"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 8,
"pageNumber": 4,
"pageSize": 2,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 15926,
"totalElements": 31851,
"size": 2,
"number": 4,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": false,
"numberOfElements": 2,
"empty": false
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。