- 1. 为什么需要spring cloud ?
- 2. pring cloud概述
- 3. 微服务场景模拟
- 4. Eureka注册中心-入门
1. 为什么需要spring cloud ?
1.1 Monolith(单体应用)架构
1.1.1 什么是单体应用?
通常情况下,这个服务所对应的代码由多个项目所组成,各个项目会根据自身所提供功能的不同具有一个明确的边界。在编译时,这些项目将被打包成为一个个JAR包,并最终合并在一起形成一个WAR包。接下来,我们需要将该WAR包上传到Web容器中,解压该WAR包,并重新启动服务器。在执行完这一系列操作之后,我们对服务的编译及部署就已经完成了。这种将所有的代码及功能都包含在一个WAR包中的项目组织方式被称为Monolith
1.1.2 单体应用的缺点
在项目很小的情况下这种单体应用比较简单,但是随着项目越变越大,代码越来越多。就会存在以下缺点:
- 编译难,部署难,测试难
- 技术选择难
- 扩展难
1.2 MicroService(微服务)架构
1.2.1 为什么需要MicroService架构
微服务架构可以解决单体项目缺点。
1.2.2 什么是MicroService架构
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被技术选项,独立开发,独立部署,独立运维,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
1.2.3 微服务远程调用方式
常见的远程调用方式有以下2种:
- RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型
- Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。
现在热门的Rest风格,就可以通过http协议来实现
1.2.3.1 认识RPC
RPC,即 Remote Procedure Call(远程过程调用),是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。说得通俗一点就是:A计算机提供一个服务,B计算机可以像调用本地服务那样调用A计算机的服务。
通过上面的概念,可以知道,实现RPC主要是做到两点:
- 实现远程调用其他计算机的服务
- 要实现远程调用,肯定是通过网络传输数据。A程序提供服务,B程序通过网络将请求参数传递给A,A本地执行后得到结果,再将结果返回给B程序。这里需要关注的有两点:
- 1)采用何种网络通讯协议?
- 现在比较流行的RPC框架,都会采用TCP作为底层传输协议
- 2)数据传输的格式怎样?
- 两个程序进行通讯,必须约定好数据传输格式。就好比两个人聊天,要用同一种语言,否则无法沟通。所以,我们必须定义好请求和响应的格式。另外,数据在网路中传输需要进行序列化,所以还需要约定统一的序列化的方式。
- 1)采用何种网络通讯协议?
- 要实现远程调用,肯定是通过网络传输数据。A程序提供服务,B程序通过网络将请求参数传递给A,A本地执行后得到结果,再将结果返回给B程序。这里需要关注的有两点:
- 像调用本地服务一样调用远程服务
- 如果仅仅是远程调用,还不算是RPC,因为RPC强调的是过程调用,调用的过程对用户而言是应该是透明的,用户不应该关心调用的细节,可以像调用本地服务一样调用远程服务。所以RPC一定要对调用的过程进行封装
1.2.3.2 认识Http
Http协议:超文本传输协议,是一种应用层协议。规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议。说到这里,大家可能觉得,Http与RPC的远程调用非常像,都是按照某种规定好的数据格式进行网络通信,有请求,有响应。没错,在这点来看,两者非常相似,但是还是有一些细微差别。
- RPC并没有规定数据传输格式,这个格式可以任意指定,不同的RPC协议,数据格式不一定相同。
- Http中还定义了资源定位的路径,RPC中并不需要
- 最重要的一点:RPC需要满足像调用本地服务一样调用远程服务,也就是对调用过程在API层面进行封装。Http协议没有这样的要求,因此请求、响应等细节需要我们自己去实现。
- 优点:RPC方式更加透明,对用户更方便。Http方式更灵活,没有规定API和语言,跨语言、跨平台
- 缺点:RPC方式需要在API层面进行封装,限制了开发的语言环境。
1.2.3.3 如何选择微服务的远程调用方式
-
RPC和Http的比较:
- (1)速度来看。RPC要比http更快,虽然底层都是TCP,但是http协议的信息往往比较臃肿,不过可以采用gzip压缩。
- (2)难度来看。RPC实现较为复杂,http相对比较简单
- (3)灵活性来看。http更胜一筹,因为它不关心实现细节,跨平台、跨语言。
-
使用场景分析
- (1)如果对效率要求更高,并且开发过程使用统一的技术栈,那么用RPC还是不错的。 比如dubbo
- (2)如果需要更加灵活,跨语言、跨平台,显然http更合适。比如springcloud
-
如何选择?
微服务,更加强调的是独立、自治、灵活。而RPC方式的限制较多,因此微服务框架中,一般都会采用基于Http的Rest风格服务。
1.2.3.4 Http客户端工具
http客户端工具有很多种,比如以下几种:
- HttpClient
- OKHttp
- URLConnection
- Spring的RestTemplate
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默认的)
1.2.4 优势
-
复杂度可控:
在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂 度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开 发效率。
-
独立部署:
由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。
-
技术选型灵活:
微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。
- 容错:
当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。
- 扩展性
单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。
1.3 MonolithµSrvice使用场景
1.3.1 选择依据
在刚开始的阶段,使用Microservice架构模式开发应用的效率明显低于Monolith。但是随着应用规模的增大,基于Microservice架构模式的开发效率将明显上升,而基于Monolith模式开发的效率将逐步下降。
原因有以下几点:
- 因为Microservice是一个架构模式,而不是一个特定的技术解决方案。其并不会将开发中的各个难点全部转移,而只是允许通过更为合适的技术来适当简化单个子服务的开发,或者绕过开发中可能遇到的部分难点。但是为了支持各个子服务的运行,我们还需要创建一系列公共服务。这些公共服务需要在编写第一个子服务的同时进行。这是导致Microservice架构模式在开发初期会具有较低效率的一个原因。
- 然而使用特定技术并不会绕过开发中所能遇到的所有难点。由于在Microservice架构中,各个子服务都集中精力处理本身的业务逻辑,而所有的公共功能都交由公共服务来完成,因此公共服务在保持和各个子服务的松耦合性的同时还需要提供一个足够通用的,能够在一定程度上满足所有当前和未来子服务要求的解决方案。而这也是导致Microservice架构模式在开发初期会具有较低效率的另外一个原因。
- 而在开发的后期,随着Monolith模式中应用的功能逐渐变大,增加一个新的功能会影响到该应用中的很多地方,因此其开发效率会越来越差。反过来,由于Microservice架构模式中的各个子服务所依赖的公共服务已经完成,而且子服务本身可以选择适合自己的实现技术,因此子服务的实现通常只需要关注自身的业务逻辑即可。这也是Microservice架构模式在后期具有较高效率的原因。
- 当我们再次通过Microservice架构模式搭建应用的时候,其在开发时的效率劣势也将消失,原因就是因为在前一次基于Microservice架构模式开发的时候,我们已经创建过一次公共服务,因此在这个新的应用中,我们将这些公共服务拿来并稍事改动即可:
1.3.2 选型
单体应用架构:中小型项目
微服务架构:大型项目
1.4 MicroService实现技术
1.4.1 MicroService只是一种架构思想
是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。
1.4.2 实现技术-springboot
Java中可以使用传统ssm ssj等架构,当然更加推荐Springboot。Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。
1.4.3 服务治理框架
由于微服务架构中存在多个微服务,那么如何管理和协调这些服务呢?就需要服务治理框架,而springcloud就是一个基于Spring Boot实现的服务治理工具包。
1.5 小结
微服务只是一种架构思想
单体应用架构:中小型项目
微服务架构:大型项目
Springcloud是用来管理和协调微服务架构中多个服务.
2. pring cloud概述
2.1 spring cloud是什么?
Spring cloud是一个基于Spring Boot实现的服务治理工具包,用于微服务架构中管理和协调服务的。
2.2 spring cloud的组成部分
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Springcloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Spring Cloud 的5个主要组成:
- 服务发现——Netflix Eureka
- 客服端负载均衡——Netflix Ribbon\Feign
- 断路器——Netflix Hystrix
- 服务网关——Netflix Zuul
- 分布式配置——Spring Cloud Config
Spring Cloud 的19个所有组成(也在不断增加):
- Spring Cloud Config 配置中心,利用git集中管理程序的配置。
- Spring Cloud Netflix 集成众多Netflix的开源软件
- Spring Cloud Bus 消息总线,利用分布式消息将服务和服务实例连接在一起,用于在一个集群中传播状态的变化
- Spring Cloud for Cloud Foundry 利用Pivotal Cloudfoundry集成你的应用程序
- Spring Cloud Cloud Foundry Service Broker 为建立管理云托管服务的服务代理提供了一个起点。
- Spring Cloud Cluster 基于Zookeeper, Redis, Hazelcast, Consul实现的领导选举和平民状态模式的抽象和实现。
- Spring Cloud Consul 基于Hashicorp Consul实现的服务发现和配置管理。
- Spring Cloud Security 在Zuul代理中为OAuth2 rest客户端和认证头转发提供负载均衡
- Spring Cloud Sleuth SpringCloud应用的分布式追踪系统,和Zipkin,HTrace,ELK兼容。
- Spring Cloud Data Flow 一个云本地程序和操作模型,组成数据微服务在一个结构化的平台上。
- Spring Cloud Stream 基于Redis,Rabbit,Kafka实现的消息微服务,简单声明模型用以在Spring Cloud应用中收发消息。
- Spring Cloud Stream App Starters 基于Spring Boot为外部系统提供spring的集成
- Spring Cloud Task 短生命周期的微服务,为SpringBooot应用简单声明添加功能和非功能特性。
- Spring Cloud Task App Starters
- Spring Cloud Zookeeper 服务发现和配置管理基于Apache Zookeeper。
- Spring Cloud for Amazon Web Services 快速和亚马逊网络服务集成。
- Spring Cloud Connectors 便于PaaS应用在各种平台上连接到后端像数据库和消息经纪服务。
- Spring Cloud Starters (项目已经终止并且在Angel.SR2后的版本和其他项目合并)
- Spring Cloud CLI 插件用Groovy快速的创建Spring Cloud组件应用。
2.3 Spring cloud pk dubbo
-
公司的背景。
Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司;Spring Cloud是大名鼎鼎的Spring家族的产品。阿里巴巴是一个商业公司,虽然也开源了很多的顶级的项目,但从整体战略上来讲,仍然是服务于自身的业务为主。Spring专注于企业级开源框架的研发,不论是在中国还是在世界上使用都非常广泛,开发出通用、开源、稳健的开源框架就是他们的主业。
-
社区活跃度。
Dubbo虽然也是一个非常优秀的服务治理框架,并且在服务治理、灰度发布、流量分发这方面做的比Spring Cloud还好,除过当当网在基础上增加了rest支持外,已有两年多的时间几乎都没有任何更新了。在使用过程中出现问题,提交到github的Issue也少有回复。
Spring Cloud自从发展到现在,仍然在不断的高速发展,从github上提交代码的频度和发布版本的时间间隔就可以看出,现在Spring Cloud即将发布2.0版本,到了后期会更加完善和稳定。
-
整个大的平台架构。
dubbo框架只是专注于服务之间的治理,如果我们需要使用配置中心、分布式跟踪这些内容都需要自己去集成,这样无形中使用dubbo的难度就会增加。Spring Cloud几乎考虑了服务治理的方方面面,更有Spring Boot这个大将的支持,开发起来非常的便利和简单。
-
技术发展的角度。
Dubbo刚出来的那会技术理念还是非常先进,后来Dubbo一直停滞不前,自然有些掉队。
Spring 推出Spring Boot/Cloud也是因为自身的很多原因。Spring最初推崇的轻量级框架,随着不断的发展也越来越庞大,随着集成项目越来越多,配置文件也越来越混乱,慢慢的背离最初的理念。随着这么多年的发展,微服务、分布式链路跟踪等更多新的技术理念的出现,Spring急需一款框架来改善以前的开发模式,因此才会出现Spring Boot/Cloud项目,我们现在访问Spring官网,会发现Spring Boot和Spring Cloud已经放到首页最重点突出的三个项目中的前两个,可见Spring对这两个框架的重视程度。
总结以上:dubbo曾经很牛逼,Spring Cloud是站在近些年技术发展之上进行开发,因此更具技术代表性。
2.4 小结
Spring cloud是微服务架构中服务治理工具集,有很多产品组成。核心为五大组成。相较于dubbo更加靠谱。
下一代微服务趋势是网格服务(service mesh)!
3. 微服务场景模拟
3.1 引入
简单步骤:
- 搭建环境
- 服务提供者=提供服务
- 服务消费者=调用服务
3.2 搭建环境
3.2.1 创建项目
项目名:springcloud_parent
3.2.2 结构分析
- user_interface/facade 用来存放公共代码
domain,query,接口,util
- user_provider_8001 :服务提供者
public User getUser
- user_consumer_9001:服务消费者
User user = 调用代码
3.2.3 搭建父模块
限定springboot版本
限定springcloud版本
<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>Finchley.SR1</spring-cloud.version>
<springboot.version>2.0.5.RELEASE</springboot.version>
</properties>
<!--用来管理版本,子模块就不需要写版本-->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.3 公共代码子模块
3.3.1 工程说明
user_interface
用来存放公共代码,domain,query,接口,util
3.3.2 创建子模块user_interface用来存放公共代码
- 准备domain中的User类
package cn.wangningbo.domain;
public class User {
private Long id;
private String name;
//提供get、set、构造方法、toString方法
}
3.4 服务提供者
3.4.1 工程说明
user_provider_8001
服务提供者,public User getUser
3.4.2 导入jar
<!--依赖于公共代码模块-->
<dependency>
<groupId>cn.wangningbo</groupId>
<artifactId>user_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springboot支持-->
<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>
</dependency>
3.4.3 yml
server:
port: 8001
spring:
application:
name: USER-PROVIDER # 注意:不要使用下划线
3.4.4 入口类
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProviderApp8001 {
public static void main(String[] args) {
SpringApplication.run(ProviderApp8001.class, args);
}
}
3.4.5 写服务代码(模拟操作数据库)
package cn.wangningbo.controller;
import cn.wangningbo.domain.User;
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("/provider/user")
public class UserController {
@GetMapping("/{id}")
public User getById(@PathVariable("id") Long id) {
//以后要通过service操作数据库获取数据,现在只是模拟
return new User(id, "二狗");
}
}
3.4.6 测试
启动入口类,使用浏览器访问:http://localhost:8001/provider/user/1
3.5 服务消费者
3.5.1 创建项目
user_consumer_9001
服务消费者
User user = 调用代码
3.5.2 导入jar
<!--依赖于公共代码模块-->
<dependency>
<groupId>cn.wangningbo</groupId>
<artifactId>user_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springboot支持-->
<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>
</dependency>
3.5.3 yml
server:
port: 9001
spring:
application:
name: USER-CONSUMER # 注意:不要使用下划线
3.5.4 入口类
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApp9001 {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp9001.class, args);
}
}
3.5.5 配置类,配置客户端
package cn.wangningbo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
//配置RestTemplate //org.springframework.web.client.RestTemplate;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.5.6 调用接口
package cn.wangningbo.controller;
import cn.wangningbo.domain.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/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
private static final String URL_PREFIX = "http://localhost:8001/";
@GetMapping("/{id}")
public User getById(@PathVariable("id") Long id) {
String url = URL_PREFIX + "provider/user/" + id;
return restTemplate.getForObject(url, User.class);
}
}
3.5.7 测试
启动ConsumerApp9001,浏览器访问地址:http://localhost:9001/consumer/user/1
注意:ConsumerApp9001调用了ProviderApp8001,所以使用ConsumerApp9001接口的时候ProviderApp8001必须处于启动状态,否则调用不到ProviderApp8001的接口!ConsumerApp9001就会没有数据!
总结
通过springboot实现服务提供者,然后再服务消费者以restTemplate以restful的方式完成服务调用。
4. Eureka注册中心-入门
4.1 Eureka注册中心介绍
4.1.1 为什么需要Eureka注册中心?
上面那种没有注册中心的SpringCloud存在着很多缺点:
- 在consumer中,我们把url地址硬编码到了代码中,不方便后期维护
- consumer需要记忆user-service的地址,如果出现变更,可能得不到通知,地址将失效
- consumer不清楚user-service的状态,服务宕机也不知道
- user-service只有1台服务,不具备高可用性
- 即便user-service形成集群,consumer还需自己实现负载均衡
当然,上面说的问题,概括一下就是分布式服务必然要面临的问题:
- 服务管理
- 如何自动注册和发现
- 如何实现状态监管
- 如何实现动态路由
- 服务如何实现负载均衡
- 服务如何解决容灾问题
- 服务如何实现统一配置
上面所面临的问题,注册中心可以实现解决
4.1.2 常见的注册中心
由于Spring Cloud为服务治理做了一层抽象接口,所以在Spring Cloud应用中可以支持多种不同的服务注册中心,比如:Netflix Eureka、Consul、Zookeeper。在Spring Cloud服务治理抽象层的作用下,我们可以无缝地切换服务治理实现,并且不影响任何其他的服务注册、服务发现、服务调用等逻辑。
尽管注册中心有那么多,我选择一个好用的Eureka
4.1.3 Eureka是什么?
Eureka是netflix的一个子模块,也是核心模块之一,Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现和注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务,而不需要修改服务调用的配置文件了,功能类似于dubbo的注册中心,比如zookeeper。
4.1.4 Eureka原理
Eureka采用了C-S的设计架构。Eureka Server作为服务注册功能的服务器,它是服务注册时中心。
而系统中的其他微服务,使用eureka的客户端连接到eureka server并维持心跳连接。这样系统的维护人员就可以通过eureka server来监控系统中各个微服务是否正常运行。SpringCloud的一些其他模块就可以通过eureka server来发现系统中的其他微服务,并执行相关的逻辑。
三大角色:
- Eureka server提供服务注册和发现
Eureka Server提供服务注册服务。各个服务提供者节点启动后,会在Eureka Server中进行注册,这样Eureka server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到 - Service Provider服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到。(Client)
- Service Consumer服务消费方从Eureka获取注册服务列表,从而能够消费服务。(Client)
Eureka client是一个java客户端,用于简化eureka server的交互,在应用启动后,将会向Eureka Server发送心跳。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表把这个服务节点移除。
4.2 Eureka注册中心搭建
注册中心的原理上面已经理解到了,下面只需要在上面项目的基础上进行一些改动,就可以变成使用注册中心版本的!
4.2.1 创建项目
eureka_server_7001
4.2.2 pom.xml
<!--springboot支持-->
<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>
</dependency>
<!--Eureka服务端支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
4.2.3 yml
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false #是否要注册到eureka 自己本身就是eureka,无需注册
fetchRegistry: false #表示是否从Eureka Server获取注册信息,自己是eureka服务端,无需获取注册信息
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka #单机配置 效果同 http://localhost:7001/eureka
4.2.4 主类
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //标识是eureka服务端
public class EnrekaServerApp7001 {
public static void main(String[] args) {
SpringApplication.run(EnrekaServerApp7001.class, args);
}
}
4.2.5 启动并且测试
启动主类,打开浏览器访问地址:http://localhost:7001/
4.2.6 注意事项
4.2.6.1 自我保护引发的错误
访问http://localhost:7001/,网页上会报这个错
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
Eureka自我保护机制导致,不影响使用.
4.2.6.2 显示IP地址
解决方案:在客户端实现
instance:
prefer-ip-address: true # 在注册中心显示Eureka客户端真实ip地址
server:
port: 8011
spring:
application:
name: USER-PROVIDER #不要使用下划线
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true # 在注册中心显示Eureka客户端真实ip地址
4.3 服务提供者注册到Eureka
4.3.1 导入jar
在上面的基础上,客户端再导入jar。eureka客户端支持
<!--eureka客户端支持 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.3.2 修改配置application.yml
注意:这里叫修改配置,有些东西可写可不写,不写会有默认值
application.yml新加入的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
application.yml最终的配置
server:
port: 8001
spring:
application:
name: USER-PROVIDER # 注意:不要使用下划线
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
4.3.3 启动类启用Eureka
@EnableEurekaClient //表示是eureka的客户端
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //表示是eureka的客户端
public class ProviderApp8001 {
public static void main(String[] args) {
SpringApplication.run(ProviderApp8001.class, args);
}
}
4.3.4 测试
先启动eureka服务端,也就是上面那个7001端口的入口类,再启动客户端服务提供者,也就是那个端口配置是8001的客户端服务提供者,这样就可以注册到注册中心了!
浏览器访问地址:http://localhost:7001/
可以再Instances currently registered with Eureka下面看到刚才注册的服务提供者,名字是USER-PROVIDER,这个名字叫什么是从那个服务的application.yml里面指定的,并且端口是8001也是在那里指定的。
4.4 服务消费者从Eureka调用服务
4.4.1 在原有的基础上再导入jar
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.4.2 修改配置application.yml
新加入配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
application.yml最终配置
server:
port: 9001
spring:
application:
name: USER-CONSUMER # 注意:不要使用下划线
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
4.4.3 入口类用Eureka客户端
@EnableEurekaClient //表示是eureka的客户端
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //表示是eureka的客户端
public class ConsumerApp9001 {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp9001.class, args);
}
}
4.4.4 整改服务的调用
package cn.wangningbo.controller;
import cn.wangningbo.domain.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/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;// org.springframework.cloud.client.discovery.DiscoveryClient;
private static final String URL_PREFIX = "http://localhost:8001/";
@GetMapping("/{id}")
public User getById(@PathVariable("id") Long id) {
//获取特定名称服务实例们
List<ServiceInstance> instances = discoveryClient.getInstances("user-provider");
//获取到第一个
ServiceInstance serviceInstance = instances.get(0);
//拼接地址
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "/provider/user/" + id;
//访问
return restTemplate.getForObject(url, User.class);
}
}
4.4.5 测试
按照顺序启动:
- 启动注册中心Eureka服务端,也就是上面那个端口是7001的那个入口类启动
- 启动注册中心Eureka客户端服务提供者,也就是上面那个端口是8001的那个入口类启动
- 启动注册中心Eureka客户端服务消费者,也就是上面那个端口是9001的那个入口类启动
浏览器地址栏访问http://localhost:7001/,可以在Instances currently registered with Eureka看到注册了两个客户端
浏览器地址栏访http://localhost:9001/consumer/user/1,请求正常可以拿到数据
网友评论