HTTP调用服务。
通过Rest风格的链接去调用远程服务。SpringBoot内置的RestTemplate模板,可以通过该模板的方法实现简单的http调用。首先实现服务的提供者(不再赘述)。
然后实现服务的消费者。消费者需要注册RestTemplate的bean。然后通过该bean实现远程调用。
@Controller
public class DeptCustomerController {
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX="http://localhost:8001";
@ResponseBody
@GetMapping("/getOne/{id}")
public Dept getOne(@PathVariable Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/getDeptById/"+id,Dept.class);
}
@ResponseBody
@GetMapping("/getAll")
public List<Dept> getAll(){
return restTemplate.getForObject(REST_URL_PREFIX+"/deptList",List.class);
}
}
Eureka服务注册与发现
遵循ap原则,基于Rest服务
Eureka包含两个组件:Eureka Server 和Eureka Client
Eureka Server 提供服务注册,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server中的注册服务表中会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的使用轮询负载算法的负载均衡器。在应用启动后,将会向EureServe发送心跳(默认周期是30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,EurekaServer将会从注册表中把这个服务节点移除。(默认周期为90秒,也就是三个心跳周期)
Eureka三大角色
服务中心(Eureka Server),提供者(Service Provider),消费者(Service Consumer)
Eureka配置注册中心
首先导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
然后在yaml配置文件中配置注册中心:
server:
port: 7001
#Eureka 配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向注册中心注册自己
fetch-registry: false #如果为false 则便是自己是注册中心
service-url: #注册中心地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
然后在启动器上方开启Eureka服务注解
@SpringBootApplication
@EnableEurekaServer //表示开启Eureka服务
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
Eureka提供者注册服务,配置服务信息
首先还是在提供者模块中导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后在yaml配置文件中配置信息
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #这里是注册中心的url
instance:
instance-id: springCloud-provider-dept-8001 #修改Eureka的描述信息
#info配置
info:
app.name: liangbo-springCloud //一些描述信息
在团队开发中,一般需要将服务的配置信息展示出去
@Autowired
DiscoveryClient discoveryClient;
@GetMapping("/dept/discover")
public Object discover(){
return discoveryClient.getInstances("springCloud-provider-dept");
}
使用DiscoveryClient来得到该服务的配置信息。最后在启动类上开启注解
@SpringBootApplication
@MapperScan("com.lv.core.mapper")
@EnableEurekaClient //在服务启动后,自动注册到Eureka注册中心
@EnableDiscoveryClient //开启配置信息
public class DeptProvider {
public static void main(String[] args) {
SpringApplication.run(DeptProvider.class,args);
}
}
运行起来,此时我们打开注册中心页面,就会发现服务已经被注册了。
红色的字体出现的原因是,Eureka的自我保护机制。它的架构哲学是宁可保存全部的微服务,也不盲目的删除任何一个可能健康的微服务。在一个心跳周期内,如果Eureka没有收到某个节点的心跳信息,它不会直接删除掉该节点,而是等待。当三个周期过去后,才会删除该节点。但如果Eureka在短时间内失去多个节点的心跳,Eureka的自我保护机制就会触发,它会认为是网络故障导致健康的微服务无法通信,这时他不会删除任何节点。会等待节点重新连接。这样的机制保证了Eureka的安全性和稳定性。
Eureka与Zookeeper的对比
CAP原则
- C:强一致性
- A:可用性
- P:分区容错性
CPA核心理论:
一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求
根据CAP原理,将NOSQL数据库分成了三大类:CA,CP,AP
作为分布式服务注册中心,Eureka和Zookeeper的区别
由于分区容错性是分布式系统中必须要保证的,所以我们需要在一致性和可用性之间权衡
- zookeeper保证的是cp,满足一致性,分区容错性的系统,通常性能不是特别高
- eureka保证的是ap,满足可用性,分区容错性,通常可能对一致性的要求比较低
Eureka集群
通过多个注册中心互相关联来打造一个Eureka集群。
server:
port: 7001
#Eureka 配置
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false #表示是否向注册中心注册自己
fetch-registry: false #如果为false 则便是自己是注册中心
service-url: #注册中心地址
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
如上互相引用,然后在服务提供方注册到所有的注册中心,这样就达成了集群。
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springCloud-provider-dept-8001 #修改Eureka的描述信息
Ribbon客户端负载均衡
主要功能是提供客户负载均衡的算法。
负载均衡简单来说就是将用户的请求平摊到多个服务商,从而达到系统的HA(高可用)
常见的负载均衡有Nginx Lvs
负载均衡简单分类:
- 集中式LB
- 进程式LB
集中式:在服务的提供方和消费方之间使用独立的设施,如Nginx(反向代理服务器),由该设施负责把访问请求通过某种策略转发之服务的提供方!
进程式:将LB的逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。Ribbon就属于进程式LB,它只是个类库,集成于消费方进程,消费方通过它老获取到服务提供方的地址。
集成Ribbon
首先向消费方导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
然后在yaml配置文件中配置Eureka
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7002/eureka/,http://eureka7003.com:7003/eureka/
然后在RestTemplate模板中集成Ribbon
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //ribbon配置restTemplate负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
在原来的rest调用中,将提供方的地址改变为服务名
@Controller
public class DeptCustomerController {
@Autowired
private RestTemplate restTemplate;
//通过Ribbon实现负载均衡的时候,应该是一个变量,通过服务名来访问
// private static final String REST_URL_PREFIX="http://localhost:8001";
private static final String REST_URL_PREFIX="http://springCloud-provider-dept";
@ResponseBody
@GetMapping("/getOne/{id}")
public Dept getOne(@PathVariable Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/getDeptById/"+id,Dept.class);
}
@ResponseBody
@GetMapping("/getAll")
public List<Dept> getAll(){
return restTemplate.getForObject(REST_URL_PREFIX+"/deptList",List.class);
}
最后在启动类上方开启Eureka客户端服务
@SpringBootApplication
@EnableEurekaClient
public class DeptCustomer {
public static void main(String[] args) {
SpringApplication.run(DeptCustomer.class,args);
}
}
然后我们生成多个服务提供方,同一服务的提供方,他们的服务名必须是一致的,但是Eureka的instance-id不能一致。
这时把所有服务跑起来,访问消费者项目,通过刷新页面可以观察到,消费方访问的接口是不断交替改变的,这就是轮询。
Ribbon负载均衡算法
可以在Ribbon集成RestTemplate的配置中,切换算法。
@Configuration
public class ConfigBean {
@LoadBalanced //ribbon配置restTemplate负载均衡
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Bean
public IRule diyRule(){
return new RandomRule();
/**
* IRule
* RoundRobinRule 默认的轮询算法
* RandomRule 随机算法
* AvailabilityFilteringRule:会先过滤掉访问故障的服务,在健康的服务中轮询
* RetryRule:会先按照轮询的策略获取服务。如果服务获取失败,则会在指定的时间内进行重试
*/
}
}
除了内置的规则,我们也可以自定义规则。
在myRule包下自定义一个配置类MyRule.java,注意:该包不要和主启动类所在的包同级。
然后在主启动类开启负载均衡并为复制指定自定义配置
//Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能加载自定义的Ribbon类(自定义的规则会覆盖原有默认的规则)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//开启负载均衡,并指定自定义的规则
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
Feign负载均衡
Feign是在Ribbon的基础上进行进一步的封装,转化为接口式的调用。
集成feign
首先在消费方导入Feign和Eureka的依赖。
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok
</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
然后配置yaml文件
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7002/eureka/,http://eureka7003.com:7003/eureka/
注册service接口,并添加@FeignClient注解,注解的值为提供方服务名称。注意service接口的方法要和提供方一致:
@Service
@FeignClient("springCloud-provider-dept")
public interface FeignService {
@GetMapping("/deptList")
List<Dept> list();
@GetMapping("/getDeptById/{id}")
Dept getById(@PathVariable("id") Long id);
}
集成RestTemplate实现负载均衡
@Configuration
public class ConfigBean {
@LoadBalanced //ribbon配置restTemplate负载均衡
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Bean
public IRule diyRule(){
return new RandomRule();
/**
* IRule
* RoundRobinRule 默认的轮询算法
* RandomRule 随机算法
* AvailabilityFilteringRule:会先过滤掉访问故障的服务,在健康的服务中轮询
* RetryRule:会先按照轮询的策略获取服务。如果服务获取失败,则会在指定的时间内进行重试
*/
}
}
最后要在主启动类上方开启Eureka和Feign
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.lv.service"}) //开启Feign并指定要扫描的包
public class FeignCustomer {
public static void main(String[] args) {
SpringApplication.run(FeignCustomer.class,args);
}
}
Hystrix断路器
服务熔断
熔断机制是对应雪崩效应的一致微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点调用正常后恢复调用链路。
springCloud中通过Hystrix实现熔断。Hystrix会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制,。熔断的注解是@HystrixCommand。
熔断机制解决如下问题:
- 当所依赖的对象不稳定是,能够快速失败。避免造成占用,导致雪崩。
- 快速失败后,能够根据一定的算法动态试探所依赖的对象是够恢复。
配置Hystrix:
在提供方导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
然后在需要熔断的方法上标识注解:@HystrixCommand 并提供备用的方法
@HystrixCommand(fallbackMethod = "htest")
@GetMapping("/getDeptById/{id}")
public Dept getDeptById(@PathVariable Long id){
Dept dept = deptService.getById(id);
if(dept==null){
throw new RuntimeException("Fail");
}
return dept;
}
public Dept htest(@PathVariable Long id){
Dept dept = new Dept();
dept.setDeptId(id);
dept.setDeptName("不存在该id的名称");
return dept;
}
最后在启动类开启Hystrix:@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableDiscoveryClient
@MapperScan("com.lv.core.mapper")
public class DeotProviderHystrix {
public static void main(String[] args) {
SpringApplication.run(DeotProviderHystrix.class,args);
}
}
服务降级
服务降级是当服务器压力剧增的时候,根据实际业务的情况以及流量,对一些服务和页面有策略性的不做处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务的正常高效运转。就是尽可能的把系统资源让给核心业务。
自动降级分类:
- 超时降级 :主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
- 失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
- 故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
- 限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
案例:
首先我们在fergn客户端的service包内新建FallbackFactory接口的实现类,重写create()方法,并将该类注册到spring中
@Component
public class ClientServiceFallBackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
return new FeignService() {
@Override
public List<Dept> list() {
return null;
}
@Override
public Dept getById(Long id) {
Dept dept = new Dept();
dept.setDeptId(id);
dept.setDeptName("这个服务已经被暂时关闭!");
return dept;
}
};
}
}
然后在服务层指定fallbackFactory
@Service
@FeignClient(value = "springCloud-provider-dept",fallbackFactory = ClientServiceFallBackFactory.class)
public interface FeignService {
@GetMapping("/deptList")
List<Dept> list();
@GetMapping("/getDeptById/{id}")
Dept getById(@PathVariable("id") Long id);
}
最后在yaml配置文件中开启降级
feign:
hystrix:
enabled: true
当我们关闭服务提供者时,界面就会显示我们想要的信息,而不是无响应
熔断和降级的区别
- 熔断:服务端,某个服务超时或者异常,引起熔断。
- 降级:客户端,从负荷考虑,干预式暂时关闭某些微服务。
- 触发原因不一样,服务熔断一般是因为服务的故障引起的自我触发的,而降级是具有代码入侵性的。
- 管理目标的层次不一样,熔断是一个框架级别的处理,每个微服务都需要,没有层级之分。而降级一般需要对业务有层级之分。
熔断,降级,限流:
限流:限制并发的请求访问量,超过阈值则拒绝;
降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;
熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复
DashBoard流监控
可以监控Hystrix的服务流
首先创建监控项目
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
然后在主启动类开启监控:@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard
public class DashBoard9001 {
public static void main(String[] args) {
SpringApplication.run(DashBoard9001.class,args);
}
}
然后在被监控的提供方添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
然后在被监控方主启动类添加一个Servlet
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableDiscoveryClient
@MapperScan("com.lv.core.mapper")
public class DeotProviderHystrix {
public static void main(String[] args) {
SpringApplication.run(DeotProviderHystrix.class,args);
}
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet (){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
return servletRegistrationBean;
}
}
然后启动节点后,进入监控见面:http:localhost:9001/hystrix
输入被监控的页面 就可以得到该流的监控仪表板
![监控](https://img-blog.csdnimg.cn/ce00ef69bf8a43b092a9617188d87fd8.png)注意!!!只能监控Hystrix熔断的项目。如果界面无法获得监控,请先触发一次熔断。
Zuul网关
zuul包含了对请求的路由和过滤两个最主要的功能。
其中路由功能负责将外部请求转发到具体的微服务上,是实现外部访问统一入口的基础。而过滤器功能则是负责对请求的过程进行干预,实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行真核,将zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
创建zuul项目
导入依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置yaml
server:
port: 9527
spring:
application:
name: springCloud-zuul
eureka:
instance:
instance-id: springCloud-zuul-9527
prefer-ip-address: true
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7002/eureka/,http://eureka7003.com:7003/eureka/
zuul:
ignored-services: "*" #忽略所有服务名
routes:
mydept.serviceId: customer
mydept.path: /mydept/**
在启动类上添加注解 开启网关代理 @EnableZuulProxy
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication9527.class,args);
}
}
SpringCloud Config 分布式配置
springCloud config 为分布式系统中的外部配置提供服务器和客户端的支持。
为微服务架构中的微服务提供集中化的外部支持,配置服务器为各个不同的微服务应用的所有环节提供了一个中心化的配置。
服务端
服务端也称为分布式配置中心,他是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端
客户端则是通过指定的配置中心来管理应用资源,以及业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
入门案例
首先创建服务端:
创建模块并导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
配置yaml
server:
port: 3344
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/liangboa/spring-cloud-config.git #配置仓库的地址
application:
name: configServer
然后在主启动类上开启服务:@EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigServer3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigServer3344.class,args);
}
}
客户端
在需要配置的微服务项目中,导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
然后清空原本的yaml配置,新建bootstrap.yaml配置文件
在bootstrap中配置:
spring:
cloud:
config:
name: provider8001-config #配置文件的名称,不需要后缀
label: master #拉取的配置所在的分支
profile: test #配置的环境
uri: http://localhost:3344 #configServer的地址
这样就完成了!
总结,以及日后学习
Eureka,Zuul,技术停止更新,用Nocos和getway代替。可以往这方面学习。