- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
读此篇前必先去了解 :
微服务介绍(史上最全)
初始SpringCloud微服务
以上都没问题了,那么接下来的学习才会轻松
微服务是一种架构方式,最终肯定需要技术架构去实施。
微服务的实现方式很多,但是最火的莫过于Spring Cloud了。为什么?
Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。
SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:
以上只是其中一部分
在搭建 eureka-server服务前我们 先来了解下 Eureka的机制
就好比是中间商 卖房子的(服务) 到中间商登记 买房子到 中间商哪里去买 这样买房子的人就有很多选择了
而 eureka 还能有效防止 服务宕机的问题 需要集群
注册中心服务端主要对外提供了三个功能:
服务注册
服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表
提供注册表
服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表
Register: 服务注册
服务的提供者,将自身注册到注册中心,服务提供者也是一个 Eureka Client。当 Eureka Client 向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。
Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。
自我保护机制
默认情况下,如果 Eureka Server 在一定的 90s 内没有接收到某个微服务实例的心跳,会注销该实例。但是在微服务架构下服务之间通常都是跨进程调用,网络通信往往会面临着各种问题,比如微服务状态正常,网络分区故障,导致此实例被注销。
固定时间内大量实例被注销,可能会严重威胁整个微服务架构的可用性。为了解决这个问题,Eureka 开发了自我保护机制,那么什么是自我保护机制呢?
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 即会进入自我保护机制。
Eureka Server 触发自我保护机制后,页面会出现提示:
接下来我们搭建eureka-server 服务 用于将 服务注册
Eureka Server 进入自我保护机制,会出现以下几种情况:
(1 Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
(2 Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
(3 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
Eureka 自我保护机制是为了防止误杀服务而提供的一个机制。当个别客户端出现心跳失联时,则认为是客户端的问题,剔除掉客户端;当 Eureka 捕获到大量的心跳失败时,则认为可能是网络问题,进入自我保护机制;当客户端心跳恢复时,Eureka 会自动退出自我保护机制。 (推出保护机制后 刷新页面就没有报错了)
如果在保护期内刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,即会调用失败。对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。
通过在 Eureka Server 配置如下参数,开启或者关闭保护机制,生产环境建议打开:
eureka.server.enable-self-preservation=true
删除src,因为是父模块不需要写代码,只需要pom.xml文件作为依赖管理
SpringCloud模块的pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
<!-- JDK-->
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
<mapper.starter.version>2.1.5</mapper.starter.version>
<mysql.version>8.0.21</mysql.version>
<mybatis.version>2.1.3</mybatis.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> <!-- 通用Mapper启动器 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
公共模块可以用于通用代码和通用配置的提取,如果模块多的话,我们只需要修改公共模块的配置就行了
common的pom.xml
<dependencies>
<!-- 添加OkHttp支持 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
application-common.yml
spring:
# 配置数据库 参数
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.42.160:3306/springcloud?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
username: root
password: root
#配置 mybatis 的 实体层
mybatis:
type-aliases-package: com.**.entity
mapper-locations: classpath*:/mapper/**/*Mapper.xml # mapper映射文件位置
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #用于控制台打印sql语句
map-underscore-to-camel-case: true #开启将带有下划线的表字段 映射为驼峰格式的实体类属性
##向Eureka 中注册实例
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka
添加Eureka模块的pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
EurekaApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
// http://127.0.0.1:8000
@SpringBootApplication
@EnableEurekaServer // 声明这个应用是一个EurekaServer (必须)
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
Eureka的application.yml
server:
port: 8000
spring:
application:
name: eureka # 设置服务名称
#配置Eureka
eureka:
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
然后启动EurekaApplication
访问http://127.0.0.1:8000
不提供子模块的创建方式了,可以参考common模块
教程进行创建子模块
producer的pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- 添加公共模块-->
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
producer的application.yml
server:
port: 9000
spring:
profiles:
active: common # 使用公共模块的yml合并到当前的yml
application:
name: producer # 设置服务名称 会在Eureka中显示
ProducerApplication
@SpringBootApplication
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class, args);
}
}
UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/all")
public ResponseEntity<List<UserEneity>> getAll(){
return ResponseEntity.ok(userService.getAll());
}
}
其他的都将省略,自行补充, 我不信你学微服务连基础三层结构都不会
启动ProducerApplication
然后去eureka控制台查看
可以通过上图发现,成功将producer注册进去了
不提供子模块的创建方式了,可以参考common模块
教程进行创建子模块
consumer的pom.xml
<dependencies>
<!-- 添加公共模块-->
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
consumer的application.yml
server:
port: 9001
# 设置服务名称 会在Eureka中显示 而且其他方也要用的
spring:
profiles:
active: common
application:
name: consumer
ConsumerApplication
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
// 我们使用了OkHttp客户端,只需要注入工厂即可
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
ConsumerUserController
import com.consumer.entity.UserEneity;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient; // 这个包不要导入错了
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/user")
public class ConsumerUserController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/getUserAll")
@ResponseBody
public ResponseEntity<List<UserEneity>> getUserAll(){
// 通过在application.yml中设置的name 获取在Eureka中注册的实例
// 因为 user-service 可能不只一个 (防止宕机) 所以是集合
List<ServiceInstance> instances = discoveryClient.getInstances("producer");
// 我们 只用第一个就行了 如果第一个宕机了那么 第2个自然就变成第一个了
//如果不懂去看一下Eureka的心跳机制 上面有
ServiceInstance instance = instances.get(0);
// getUri 获取producer服务的url然后拼接成http请求以及要请求的内容
String url = instance.getUri()+"/user/all";
// 通过http请求客户端工具 将请求发送 获取请求 对应的数据
List<UserEneity> list = restTemplate.getForObject(url,List.class);
return ResponseEntity.ok(list);
}
}
其他的都将省略,自行补充
启动ConsumerApplication
然后去eureka里查看
可以通过上图发现,成功将consumer注册进去了
消费者调用生产者
访问: http://localhost:9001/user/getUserAll
如果请求成功了那么代表服务间的调用ok了
如果我们只配置一个Eureka服务端,那么如果这个服务端崩盘,那么所有服务都无法获取,这肯定不是我们不期望的。所以为了保证高可用性,我们需要搭建Eureka集群。
其实很简单,我么按照上面Eureka搭建教程在搭建2套,然后只需要修下配置文件就行
eureka: 8000 ,eureka: 8001,eureka: 8002
每个Eureka配置如下:
然后启动所有的Eureka
修改application-common.yml
从以前向单eureka改为向多个eureka注册, 这样如果其中一个eureka宕机了那么其他的eureka还能提供服务
##向Eureka 中注册实例
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka,http://127.0.0.1:8001/eureka,http://127.0.0.1:8002/eureka
测试集群
我们将8001的eureka关闭了,然后看看消费者还能请求成功吗?
访问http://127.0.0.1:8001/
发现已经进不去了,也就代表我们服务关闭成功了
然后我们消费者进行访问发现还是能访问成功的
默认注册时使用的是主机名或者localhost,如果想用ip进行注册,可以添加配置如下:
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
eureka:
instance:
lease-expiration-duration-in-seconds: 90 # 服务续约(renew)的间隔,默认为30秒
lease-renewal-interval-in-seconds: 30 #服务失效时间,默认值90秒
也就是说,默认情况下每隔30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,
EurekaServer就会认为该服务宕机,会定时(eureka.server.eviction-interval-timer-in-ms设定的时间)从服务列表中移除,这两个值在生产环境建议不要修改,默认即可。
eureka:
registry-fetch-interval-seconds: 10
当服务消费者启动时,会检测 eureka.client.fetch-registry=true (默认) ,则会从EurekaServer服务的列表拉取只读备份,然后缓存在本地。并且 每隔30秒 会重新拉取并更新数据。可以在 consumer-demo项目中通过下面的参数来修改
服务下线
当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。
这个过程死自动的无须配置
失效剔除
有时我们的服务可能由于内存溢出或网络故障等原因使得服务不能正常的工作,而服务注册中心并未收到“服务下
线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。我们可以手动配置 剔除时间
# 在eureka中注册
eureka:
server:
eviction-interval-timer-in-ms: 3 #服务关闭3秒后剔除
设置后你就能看到控制台不断在刷新Running the evict task with compensationTime xxms
自我保护
我们关停一个服务,很可能会在Eureka面板看到一条警告:
这是触发了Eureka的自我保护机制。当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。
可以通过下面的配置来关停自我保护:
# 在eureka中注册
eureka:
client:
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: ${defaultZone:http://127.0.0.1:10086/eureka}
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
关闭后管理页面会出现
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
意思是: 自保存模式被关闭。这可能不能保护实例在网络/其他问题的情况下过期。
一般情况下 开发阶段可以关掉 生产环境就别关了
点赞 -收藏-关注-便于以后复习和收到最新内容有其他问题在评论区讨论-或者私信我-收到会在第一时间回复如有侵权,请私信联系我感谢,配合,希望我的努力对你有帮助^_^
我们正在创建一个 n 层 Silverlight LOB 应用程序,并且正在考虑使用 .NET RIA 服务。我们不清楚这与我们当前的 WCF 服务 API 的关系在哪里。我们当前的架构是: 银光
上下文:我在celery + rabbitmq堆栈上有一个主工作系统。 系统已docker化(此处未提供worker服务) version: '2' services: rabbit:
我是 Windows Azure 新手,我正在尝试将我的 Web 应用程序部署到 Windows Azure。在我的应用程序中,我使用了一些 Web 服务,现在我想知道如何在 Windows Azur
因此,根据我对服务的了解,自定义对象似乎是写入服务以返回数据的方式。如果我正在编写将用于 1) 填充数据库或 2) 为网站提供信息的服务,是否有返回数据集/数据表而不是包含所有这些的自定义对象列表的用
我在 google 和 stackoverflow 上都找过答案,但似乎找不到。我正在尝试将 azure 实验的输出获取到应用程序。我使用 ibuildapp 和谷歌表单制作了该应用程序。如何使用 g
我不小心删除了 kubernetes svc: service "kubernetes" deleted 使用: kubectl delete svc --all 我该怎么办?我只是想删除服务,以便
我正在努力确定解决网络服务问题的最有效方法。 我的情况:我正在开发一个 Android 应用程序,它通过 Web 服务从 mysql 数据库(在我自己的服务器 PC 上)存储和检索数据。用户按下提交按
我一直在翻阅 Android 文档,我很好奇。什么时候绑定(bind)服务而不是不绑定(bind)服务?它提供了哪些优点/限制? 最佳答案 When would you bind a service
我试图从架构的角度理解 hive,我指的是 Tom White 关于 Hadoop 的书。 我遇到了以下关于配置单元的术语:Hive Services、hiveserver2、metastore 等。
我的问题:安装服务后我无法导航到基地址,因为服务不会继续运行(立即停止)。我需要在服务器或我的机器上做些什么才能使 baseAddress 有效吗? 背景:我正在尝试学习如何使用 Windows 服务
我正在努力就 Web 服务的正确组织做出决定。我应该有多个 ASMX 来代表 Web 服务中的不同功能,还是应该有一个 ASMX? 如果我有多个 ASMX,这不构成多个 Web 服务吗? 如果我只有一
我正在从事一个在 azure 平台上提供休息服务的项目。该服务由 iPhone 客户端使用,这是选择其余方法的重要原因之一。 我们希望通过 AccessControlService(ACS) 并使用
我是 Ionic 新手,正在使用 Ionic 3.9.2 我有几个终端命令来为我的 ionic 应用程序提供服务,但是,我没有发现这两个命令之间有任何区别。 ionic serve 和 ionic s
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
作为项目的一部分,我期待着问这个问题。我过去有开发和使用 Web 服务的经验,并且非常熟悉这些服务。但是,有人告诉我,作为下一个项目的一部分,我将需要使用“安全”的 Web 服务。您能否提供一些见解,
我浏览了很多关于这个问题的信息,但找不到解决方案。这里的问题是,我想使用 Apache Cordova 和 Visual Studio 连接到 wcf。因此,如果有人找到合适的工作解决方案,请发布链接
我在 Windows 服务中托管了一个 WCF(从 MS 网站示例中选取),我可以使用 SOAP UI 访问和调用方法。但是,当我尝试使用 jquery 从 Web 应用程序调用相同的方法时,我不断收
我们构建了一个 Android 应用程序,它从 Android 向我的 PHP 服务器发送 HTTP 请求。作为响应,Web 服务将 JSON 对象发送到 Android 应用程序以显示结果。 就像其
我想在 android 应用程序中调用 soap web 服务,它需要一个枚举值作为参数,它是一个标志枚举。如何从 Android 应用程序将一些值作为标志枚举传递给此 Web 服务方法? 我使用 K
我尝试在模拟器上安装 Google Play。我已按照 Google Dev Site 中的说明进行操作. 使用 ADV 管理器似乎没问题,设备的目标是 Google API 版本 22,但是当我运行
我是一名优秀的程序员,十分优秀!