微服务概述区别SpringCloud
SpringCloud和SpringbootSpringCloud和RPC RestTemplate
服务提供者服务消费者 Eureka
Eureka Server服务注册Eureka 自我保护机制本地搭建Eureka 集群 服务消费者
Ribbon
服务消费者基于Ribbon调用服务Ribbon负载均衡策略 Feign Hystrix
服务熔断(服务端)服务降级(客户端)总结Dashboard监控 ZuulConfig
config简介git环境搭建config-serverconfig-client 微服务概述
微服务强调的是服务的大小,是具体解决某一个问题的一个服务应用;微服务架构将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,服务之间采用轻量级的HTTP通信机制互相沟通,每个服务都围绕着具体的业务进行构建,被独立的部署到生产环境中,每个微服务可以有自己的数据库,也可以有统一的数据库
区别SpringCloud SpringCloud和SpringbootSpringBoot专注开发单个个体微服务;SpringCloud是关注全局的微服务协调整理,它将SpringBoot开发的单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务
SpringCloud和RPCSpringCloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
RestTemplate详细代码
服务提供者package com.rainhey.springcloud.controller;import com.rainhey.springcloud.pojo.Dept;import com.rainhey.springcloud.service.DeptService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestControllerpublic class DeptController { @Autowired private DeptService deptService; @PostMapping("/dept/add") public boolean addDept(Dept dept){ return deptService.addDept(dept); } @GetMapping("/dept/get/{id}") public Dept get(@PathVariable long id){ return deptService.queryById(id); } @GetMapping("/dept/list") public List
package com.rainhey.springcloud.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@Configurationpublic class ConfigBean { @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); }}
package com.rainhey.springcloud.controller;import com.rainhey.springcloud.pojo.Dept;import org.springframework.beans.factory.annotation.Autowired;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;@RestControllerpublic class DeptConsumerController { //消费者不应该有service层 //RestTemplate供我们直接调用,注册到spring中 //RestTemplate 常用参数格式 @Autowired private RestTemplate restTemplate; //提供多种边界访问http服务的方法,简单的restful模板 public static final String prefixurl="http://localhost:8001"; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(prefixurl+"/dept/add", dept, boolean.class); } @RequestMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable long id){ return restTemplate.getForObject(prefixurl+"/dept/get/"+id, Dept.class); } @RequestMapping("/consumer/dept/list") public List
server: port: 7001#Eureka 配置eureka: instance: hostname: localhost #Eureka服务端的实例名称 client: register-with-eureka: false # 是否向Eureka服务中心注册自己 fetch-registry: false #如果为false,表示自己是注册中心 service-url: # 监控页面 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
添加注解@EnableEurekaServer,开启功能package com.rainhey.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication// EurekaServer 服务端启动类,注册中心@EnableEurekaServerpublic class EurekaServer_7001 { public static void main(String[] args) { SpringApplication.run(EurekaServer_7001.class, args); }}
访问 http://localhost:7001/服务注册 服务提供者添加依赖
eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: springcloud-provider-dept8001 //修改Eureka默认描述信息Status info: #autuator监控信息 app.name: rainhey-springcloud company.name: www.rainhey.com
添加注解@EnableEurekaClient,开启功能package com.rainhey.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClient //在服务启动后自动注册到Eurekapublic class DeptProvider_8001 { public static void main(String[] args) { SpringApplication.run(DeptProvider_8001.class, args); }}
访问 http://localhost:7001/Eureka 自我保护机制
默认情况下,当Eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除(默认是90秒),但是如果当一分钟内收到的心跳数大量减少时,会触发保护机制:Eureka认为虽然收不到实例的心跳,但不会把它们从注册表中删掉
保护机制的目的是避免网络连接故障,因为只有在微服务启动的时候才会发起注册请求,如果Eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到Eureka server了
创建三个Eureka Server,配置文件如图
server: port: 7001#Eureka 配置eureka: instance: hostname: eureka7001.com #Eureka服务端的实例名称 client: register-with-eureka: false # 是否向Eureka服务中心注册自己 fetch-registry: false #如果为false,表示自己是注册中心 service-url: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server: port: 7002#Eureka 配置eureka: instance: hostname: eureka7002.com #Eureka服务端的实例名称 client: register-with-eureka: false # 是否向Eureka服务中心注册自己 fetch-registry: false #如果为false,表示自己是注册中心 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
server: port: 7003#Eureka 配置eureka: instance: hostname: eureka7003.com #Eureka服务端的实例名称 client: register-with-eureka: false # 是否向Eureka服务中心注册自己 fetch-registry: false #如果为false,表示自己是注册中心 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
provider 服务提供者配置文件修改server: port: 8001mybatis: type-aliases-package: com.rainhey.springcloud.pojo config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper @Autowired private RestTemplate restTemplate; //提供多种边界访问http服务的方法,简单的restful模板 //public static final String prefixurl="http://localhost:8001"; //基于Ribbon负载均衡,通过服务名访问 public static final String prefixurl="http://SPRINGCLOUD-PROVIDER-DEPT"; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(prefixurl+"/dept/add", dept, boolean.class); } @RequestMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable long id){ return restTemplate.getForObject(prefixurl+"/dept/get/"+id, Dept.class); } @RequestMapping("/consumer/dept/list") public List
Ribbon负载均衡策略
使用Ribbon自带负载均衡策略
@Configurationpublic class ConfigBean { //配置负载均衡实现RestTemplate @LoadBalanced //ribbon @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } //要想使用自带的某种负载均衡策略,只需将其注入容器 //比如想要使用随机策略 @Bean public IRule myIRule(){ return new RandomRule(); }}
自定义负载均衡策略
在主启动类上一级建包myRule,在其中自定义负载均衡策略,注意自定义组件包不要和主启动类同级,否则会被直接扫描到,影响程序的灵活性
配置类
@Configurationpublic class MyRule { @Bean public IRule myRule(){ return new MyRandomRule();//默认是轮询RandomRule,现在自定义为自己的 }}
参照RandomRule继承类AbstractLoadBalancerRule,写自己的负载均衡策略
public class MyRandomRule extends AbstractLoadBalancerRule { private int total = 0;//被调用的次数 private int currentIndex = 0;//当前是谁在提供服务 //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE") public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List
在主启动类添加@RibbonClient(name = “SPRINGCLOUD-PROVIDER-DEPT”,configuration = MyRule.class)注解,指定服务名和负载均衡的配置文件
@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+RestTemplate的基础上做了进一步的封装,由它帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量Feign默认集成了Ribbon,Feign 本质上也是实现了 Ribbon,只不过在调用方式上,满足一些开发者习惯的接口调用习惯!
Feign相当于在中间加了一层接口,使得客户端的调用更加方便! 客户端导入以下主要依赖,API模块导入feign依赖
server: port: 8010# eureka 配置eureka: client: register-with-eureka: false #不向eureka注册自己 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
API层编写接口@Component//@FeignClient:微服务客户端注解,value指定微服务的名字@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")public interface DeptClientService { @GetMapping("/dept/get/{id}") public Dept get(@PathVariable("id") long id); @GetMapping("/dept/list") public List
@RestControllerpublic class DeptConsumerController { @Autowired private DeptClientService deptClientService; @PostMapping("/consumer/dept/add") public boolean add(Dept dept) { return deptClientService.addDept(dept); } @GetMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id) { return deptClientService.get(id); } @GetMapping("/consumer/dept/list") public List
Hystrix
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
当扇出链路的某个微服务不可用或者响应时间太长时,会熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是:@HystrixCommand
服务熔断解决如下问题:
当所依赖的对象不稳定时,能够起到快速失败的目的;快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。 添加依赖,复制一份服务提供者代码,添加Hystrix依赖
同springcloud-provider-dept-8001 的配置,无需添加额外配置服务提供方为方法提供备选方案
@HystrixCommand(fallbackMethod = "hystrixGet") @GetMapping("/dept/get/{id}") public Dept get(@PathVariable long id){ Dept dept = deptService.queryById(id); if(dept==null){ throw new RuntimeException("id="+id+"不存在该用户"); } return dept; } // 备选方案 public Dept hystrixGet(@PathVariable long id){ return new Dept().setDeptno(id) .setDname("id="+id+"没有对应信息,null @hystrix") .setDb_source("no this db in database"); }
主启动类添加熔断支持@SpringBootApplication@EnableEurekaClient //在服务启动后自动注册到Eureka//添加对熔断的支持@EnableCircuitBreakerpublic class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class, args); }}
测试:当服务消费方查询一个数据库不存在的ID服务降级(客户端)
服务降级是指当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。自我理解就是当为了把系统资源分配给优先级高的服务,而关闭某些优先级低的服务,而在客户端暂时为这些优先级低的服务提供一种回调
在API模块创建DeptClientServiceFallbackFactory类实现FallbackFactory接口,重写create方法@Componentpublic class DeptClientServiceFallbackFactory implements FallbackFactory { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept get(long id) { return new Dept().setDeptno(id) .setDname("id="+id+"当前服务降级,已经被关闭") .setDb_source("没有数据"); } @Override public List
@Component//@FeignClient:微服务客户端注解,value指定微服务的名字@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)public interface DeptClientService { @GetMapping("/dept/get/{id}") public Dept get(@PathVariable("id") long id); @GetMapping("/dept/list") public List
server: port: 8010# eureka 配置eureka: client: register-with-eureka: false #不向eureka注册自己 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/# 开启降级feign: hystrix: enabled: true
测试,可以正常访问;
当关闭服务提供者后,再次访问如图
当再次开启服务提供者后,再次访问如图
服务熔断:服务端–某个服务超时或异常,引起熔断
服务降级:客户端–从整体网站请求负载考虑,当某个服务关闭之后,服务将不再被调用,此时在客户端我们可以准备一个FallbackFactory ,返回一个默认值
创建Dashboard模块-监控页面
添加依赖
server: port: 9000
编写主启动类,添加注解@EnableHystrixDashboard开启功能@SpringBootApplication@EnableHystrixDashboardpublic class DeptConsumerDashboard { public static void main(String[] args) { SpringApplication.run(DeptConsumerDashboard.class, args); }}
启动主启动类,访问 http://localhost:9000/hystrix监控服务-在hystrix-8001模块的主启动类中添加一个 servlet,配置URL
@SpringBootApplication@EnableEurekaClient //在服务启动后自动注册到Eureka//添加对熔断的支持@EnableCircuitBreakerpublic class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class, args); } //增加一个servlet dashboard 监控 @Bean public ServletRegistrationBean hystrixMetricsStreamServlet(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream"); return servletRegistrationBean; }}
访问 http://localhost:9000/hystrix,监控信息填写如下
点击Monitor Stream
监控说明
Zuul包含了对请求的路由和过滤两个最主要功能
路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得
server: port: 9527spring: application: name: springcloud-zuuleureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ instance: instance-id: zuul9527.com #修改Eureka默认描述信息Status prefer-ip-address: true #隐藏真实IPinfo: #autuator app.name: rainhey-springcloud company.name: www.rainhey.com
编写主启动类,添加注解 @EnableZuulProxy@SpringBootApplication@EnableZuulProxypublic class ZuulApplication_9527 { public static void main(String[] args) { SpringApplication.run(ZuulApplication_9527.class, args); }}
HOSTS文件中添加 127.0.0.1和 www.rainhey.com 的映射测试直接访问
通过Zuul访问微服务
隐藏服务名,修改配置只能通过 http://www.rainhey.com:9527/mydept/dept/get/1 访问,不能再通过 http://www.rainhey.com:9527/springcloud-provider-dept/dept/get/1访问
zuul: routes: mydept.serviceId: springcloud-provider-dept #隐藏服务名 mydept.path: /mydept/** ignored-services: springcloud-provider-dept # 使通过原来的服务名访问无效 ; 可以使用通配符 "*" 来隐藏所有的服务名
通过服务名无法访问
通过 /mydept/** 访问
添加统一的访问前缀 /rainhey
zuul: routes: mydept.serviceId: springcloud-provider-dept #隐藏服务名 mydept.path: /mydept/** ignored-services: springcloud-provider-dept # 使通过原来的服务名访问无效 ; 可以使用通配符 "*" 来隐藏所有的服务名 prefix: /rainhey # 设置统一的访问前缀
Config config简介
spring cloud config 为微服务架构中的微服务提供集中化的外部支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置
spring cloud config 分为服务端和客户端两部分。
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,即微服务应用;配置服务器默认采用git来存储配置信息
git环境搭建 码云创建springcloud-config仓库,复制链接在桌面创建Git文件夹,在文件夹里面打开git bash,执行git clone ****在克隆文件springcloud-config中编写创建编写yml文件spring: profiles: active: dev---spring: profiles: dev application: name: springcloud-config-dev---spring: profiles: test application: name: springcloud-config-test
依次执行git add 、 # 将被修改的文件加入到暂存区
git commit -m "first commit" #本地提交
git push origin master #push到远程仓库,origin用户,master分支
创建config-client.ymlspring: profiles: active: dev---spring: profiles: dev application: name: springcloud-provider-depteureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka/server: port: 8202---spring: profiles: test application: name: springcloud-provider-depteureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka/server: port: 8201
上传到码云 config-server通过config-server可以连接到git,读取其中的资源和配置
导入依赖server: port: 3344spring: application: name: springcloud-config-server # 连接远程仓库 cloud: config: server: git: uri: https://gitee.com/Rainhey/springcloud-config.git
编写主启动类,添加注解 @EnableConfigServer@SpringBootApplication@EnableConfigServerpublic class config_server3344 { public static void main(String[] args) { SpringApplication.run(config_server3344.class, args); }}
测试,读取配置config-client 导入依赖
spring: cloud: config: uri: http://localhost:3344 name: config-client #需要从git上读取的资源名称,不需要后缀 profile: dev #环境 label: master # 分支
创建用户级别配置application.yml
spring: application: name: springcloud-config-client-3355
编写主启动类和controller@RestControllerpublic class ConfigClientController { @Value("${spring.application.name}") //从远程取值 private String applicationName; @Value("${eureka.client.service-url.defaultZone}") private String eurekaServer; @Value("${server.port}") private String port; @RequestMapping("/config") public String getConfig(){ return "application="+applicationName +" eurekaServer="+eurekaServer +" port="+port; }}
开启config-server和config-client测试,结果如图拿到远程配置的端口8202,以及拿到其他远程配置