为什么要用服务注册中心?
- 在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),
- 其次就是如何在不同的服务之间进行通信?
- 如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。
市面常见的注册中心(扩展)
Zookeeper(雅虎Apache)
Eureka(Netfix)
Nacos(Alibaba)
Consul(Google)
他们分别都有什么特点,我们如何进行选型呢?我们主要从社区活跃度,稳定性,功能,性能等方面进行考虑即可.本次微服务的学习,我们选择Nacos,它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好
Nacos注册中心简介
Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。其官网地址如下:https://nacos.io/zh-cn/docs/quick-start.html
Nacos本身就是基于Spring boot MVC开发的
Nacos提供了 服务的注册、发现、配置的特性
Nacos活跃度高、稳定、性能强、学习成本底
Nacos的官网是:nacos.io
Nacos在github的源码:github.com/alibaba/nacos
Nacos在windows环境下,解压即可使用(需要JDK和数据库的依赖)
Nacos下载
https://github.com/alibaba/nacos/releases
Nacos启动
Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):./startup.sh -m standalone
Windows启动命令(standalone代表着单机模式运行,非集群模式):startup.cmd -m standalone
说明:
1)执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
2)nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),
访问Nacos服务
打开浏览器,输入http://localhost:8848/nacos地址
默认账号密码: nacos/nacos
服务注册与调用入门(重点)
创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
Nacos版本
使用Nacos的版本选择可参考如下网址(涉及到一个兼容性问题,不能随意指定其版本):
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
创建java微服务项目的创建
根pom文件的配置(此pom为根节点,不做代码)
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>sca-provider</module>
<module>sca-consumer</module>
</modules>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>com.cy.jt</groupId>
<artifactId>05-jt-sca</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.cloud>Hoxton.SR8</spring.cloud>
<spring.cloud.alibaba>2.2.5.RELEASE</spring.cloud.alibaba>
</properties>
<!--依赖版本管理-->
<dependencyManagement>
<dependencies>
<!--spring cloud 依赖,此依赖需要springboot的支持,此依赖中定义了spring cloud微服务规范-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud}</version>
<!--import表示此工程下的子工程可以直接引用这个依赖中版本-->
<scope>import</scope>
<!--当scope为import时,这里的类型必须为pom类型-->
<type>pom</type>
</dependency>
<!--spring cloud alibaba 依赖,此依赖依赖于 spring cloud,当前依赖中是基于微服务规范做了具体实现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>
说明:
- 由于Maven只允许继承一个
<parent>
,为了使子项目多继承pom所以使用<dependencyManagement>
添加多个依赖的pom - 父工程资源初始化完成以后,将src目录删除,因为父工程只负责依赖管理.
创建provider服务和consumer项目
子项目配置
server:
port: 8090
spring:
application:
name: sca-consumer # 代表该项目的服务名,后续会在nacos注册中心呈现这个名字
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 从哪里去查找(注册)服务,链接到服务注册中心的地址
feign: # 启用feign方式服务调用接口时,如果出现异常则执行默认的处理机制
hystrix:
enabled: true # 新版本默认值为false:即使出现异常不会执行默认的处理机制
注意: 服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。
server-addr: 如果不写默认是:localhost:8848
当配置好以上几点,启动Nacos服务,并启动项目即可将服务(项目)注册到Nacos上
Nacos小结
1.为什么要将服务注册到nacos?
为了更好的查找这些服务,得到统一的管理
2.在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?
当启动一个项目时会向nacos以web方式请求发送一次心跳包注册到Nacos
程序会默认每5s向nacos发送一次心跳
3.对于Nacos服务来讲它是如何判定服务实例的状态?
如果检查到实例超过15s,还没发送心跳过来。则会将实例健康状态改成false
30s没有收到心跳,nacos会把项目移除。
如需重写添加,则重启项目,注册服务
4.服务启动时如何找到服务启动注册配置类?
通过依赖内置的NacosNamingService类
5.服务消费方是如何调用服务提供方的服务的?
通过RestTemplate对象
6.注册中心的核心数据是什么?
服务的名字和它对应的网络地址
7.注册中心中心核心数据的存取为什么会采用读写锁?
底层安全和性能(可查看AbstractHandlerMethodMapping类)
8.Nacos是如何保证高可用的?
断开链接后重试+本地缓存+集群
Nacos服务负载均衡设计及实现
说明:
一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,以下是Nacos实现的负载均衡实例
1.基于RestTemplate对象进行远程调用提供者的服务
实例化RestTemplate对象,并将对象加入IOC(Bean)容器
//远程调用微服务对象: 作用是基于此对象可以访问远程服务,这个对象是Spring MVC 自带的
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
通过RestTemplate对象的getForObject发送一个hppt请求
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho01() {
String url = "http://localhost:8081/provider/echo/";
//远程过程调用,向提供方发起一个请求(简称:RPC)
return restTemplate.getForObject(url, String.class);
}
说明
该方法虽然可以远程调用接口,但是不具备负载均衡特性
它不需要依赖nacos直接通过固定的ip和端口进行服务调用
此方式不适合多服务实例调用
2.基于RestTemplate进行远程服务调用
@Autowired
//负载平衡器客户端: 基于此对象可以从注册中心获取服务实例
private LoadBalancerClient loadBalancerClient;
@GetMapping("/consumer/doRestEcho2")
public String doRestEcho02() {
//基于loadBalancerClient对象传的服务名,去从nacos注册中心按照服务名查找服务实例(可能有多个)
ServiceInstance choose = loadBalancerClient.choose("sca-provider");
//查询到的服务按默认轮询的方式选择,IP+端口的服务(算法去选择服务实例)
String host = choose.getHost();//ip地址(这里ip地址不是固定的)
int port = choose.getPort();//获取端口号(这里端口号不是固定的)
String url = "http://" + host + ":" + port + "/provider/echo/";
return restTemplate.getForObject(url, String.class); //远程调用服务
}
LoadBalancerClient对象说明:
- 该对象在项目启动时会自动注入带IOC容器里,无需配置
- 它返回是ServiceInstance对象,里面包含所有按需求查询到的服务,此时会在本地按照一定算法去选择服务实例
- 默认以轮询的方式选择IP+端口的服务
说明
该方法代码量比较多,有没有一种通过少量代码实现负载均衡的访问呢?
3.通过@LoadBalanced整合RestTemplate对象实现代码的优化
将restTemplate远程调用对象与负载均衡客户端进行整合
RestTemplate对象将会自动加入LoadBalanced负载均衡功能
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate() {
return new RestTemplate();
}
// 该对象此时拥有 负载均衡客户端 + 远程过程调用对象 的功能
@Autowired
private RestTemplate loadBalancedRestTemplate;
@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03() {
// http:/nacos上注册的服务名/该服务的接口
// 当loadBalancedRestTemplate解析时会默认把服务名换成IP+端口了
String url = "http://sca_provider/provider/echo";
return loadBalancedRestTemplate.getForObject(url, String.class);
}
@LoadBalanced的作用是什么?
描述了RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,
这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略
说明
这个方法虽然简化了代码,但是不是特别高效,原因如下:
- 它虽然基于具备了负载均衡特性的RestTemplate进行远程服务调用,
- 但从效率上会稍微逊色于方式2,因为底层会对请求进行拦截,
- 拦截到以后再基于loadBalancerClient获取服务实例进行调用
小结说明
1. Nacos是如何实现负载均衡的?
这里多个实例并发提供服务的方式为负载均衡,
是由Nacos集成了Ribbon来实现的,默认的负载均衡时轮询策略
Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。
2. 什么是Ribbon?
Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡
(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息)
这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用
3. Ribbon负载均衡策略是什么
基于Ribbon方式的负载均衡,Netflix默认提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,默认的负载均衡策略是轮训策略!
共8种可以通过查看IRule接口的实现类进行分析当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.
4. Nacos中的负责均衡底层是如何实现的?
通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务
5. Ribbon 是什么
Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法,后因开源被Spring整合了
6.Ribbon 可以解决什么问题?
基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口
7.我们可以自己定义负载均衡策略吗?
可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现
基于Feign远程服务的调用与实践
背景分析:
服务消费方基于rest的请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,打码量复杂且不易维护,此时Feign诞生
Feign是什么
Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:
Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由社区进行维护,更名为OpenFeign。
为什么使用Feign
结构清晰,代码逻辑强,可重复调用
Feign的实现
1. 依赖jar包(SpringCloud团队基于OpenFeign研发了starter)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. Service层定义 Feign的远程服务调用接口
@FeignClient(name = "sca-provider",contextId = "remoteProviderService", fallbackFactory = RemoteProviderFallbackFactory.class)
public interface RemoteProviderService {
//GetMapping的参数须与远程服务的地址相同(请求方式相同)
@GetMapping("/provider/echo/{msg}")
String echoMsg(@PathVariable("msg") String msg);
}
接口说明:
这个接口不需要我们自己写实现类,底层会在服务启动时帮我们创建(代理对象),实现类内部进行远程服务调用参数说明:
name: 定义Feign访问nacos中的那个服务名下的服务器(值:为nacos中的服务名)contextId:
为一个上下文id,一般就写当前接口名, 用于区分服务调用;因为将来一个服务提供方会提供很多资源调用,假如服务消费方法基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务启动就会失败fallbackFactory:
用于指定服务终断或报异常时,应该返回给客户端默认的结果远程调用出现异常会调用该对象,
如果不指定contestId,那么Feign在注册服务到Bean时会按照name去注册,
接下来是具体的实现:
3. 配置Feign接口调用时出现的异常,默认调用的类方法
@Service
//RemoteProviderService:表示该类只负责RemoteProviderService出现的异常后执行的处理机制
public class RemoteProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
//此方法会在指定的Feign接口服务,调用失败时出现了异常,执行的方法
//throwable 用于接收异常
@Override
public RemoteProviderService create(Throwable throwable) {
return (msg) -> {
System.out.println(throwable.getLocalizedMessage());
return throwable.getMessage();
};
}
}
注意:此类在实现该FallbackFactory接口时,注意写入泛型
泛型的类是需要捕获,可能出现异常的Feign接口类
4. 开启Feign的异常处理机制
spring中配置文件设置
feign:
hystrix:
enabled: true # 开启Feign的异常处理机制
它默认值为false表示:
以feign的方式调用服务接口时,如果出现异常则执行默认是不做任何处理的
5. 主类或者配置类声明Spring cloud启动feign方式的服务调用
@EnableFeignClients
@SpringBootApplication
public class ScaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ScaConsumerApplication.class, args);
}
}
@EnableFeignClients :可描述再配置类中,又因主类默认也是配置类,所以也可以用在主类
作用:Spring cloud启动时将使用feign方式去调用nacos中的提供者的服务
实现:服务启动时自动扫描@FeignClient注解描述的接口,并基于接口创建代理对象将由Feign代理对象帮我们发起远程服务调用
Feign应用调用过程分析(底层逻辑)
- 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
- Feign Starter 在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
- 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request对象,基于此对象进行远程过程调用。
- 请求对象经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
- 通过 Client 携带 Request 调用远端服务返回请求响应。
- 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据。
Feign的总结
1.为什么使用feign?
基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用
2.@FeignClient注解的作用是什么?
告诉Feign Starter,在项目启动时,为此注解描述的接口创建实现类-代理类
3.Feign方式的调用,底层负载均衡是如何实现的?
Ribbon
4.@EnableFeignCleints 注解的作用是什么?
描述配置类,例如启动类
Nacos大总结
1.何为注册中心?
用于记录服务信息的一个web服务
2.注册中心的核心对象?
服务提供方,服务消费方,注册中心-Registry 等等
3.市面上常用注册中心?
Google-Consul,Alibaba-Nacos,…
4.微服务架构下项目的构建过程
需要使用聚合工程