官网
In short, the microservice architectural(架构) style is an approach to developing a single application as a suite(系列) of small services
, each running in its own process(进程)
and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business(业务) capabilities(单元)
and independently(独立) deployable(部署)
by fully automated deployment machinery. There is a bare(基于) minimum of centralized(分布式) management(管理) of these services
, which may be written in different programming languages and use different data storage technologies. -----[摘自官网]
官方定义:微服务就是由一系列围绕自己业务开发的微小服务构成,他们独立部署运行在自己的进程里,基于分布式的管理
通俗定义:微服务是一种架构,这种架构是将单个的整体应用程序分割成更小的项目关联的独立的服务。一个服务通常实现一组独立的特性或功能,包含自己的业务逻辑和适配器。各个微服务之间的关联通过暴露api来实现。这些独立的微服务不需要部署在同一个虚拟机,同一个系统和同一个应用服务器中。
单体项目的优点:单一架构模式在项目初期很小的时候开发方便,测试方便,部署方便,运行良好
。
缺点:
微服务架构应用
架构的演变
===>
[垂直应用架构] ===>
[分布式服务架构] ===>
[流动计算架构]||[微服务架构] ===>
[未知]dubbo官网:点击跳转
好的架构并不是设计出来的,一定是进化来的!!!
初出茅庐:2011年末,阿里巴巴在GitHub上开源了基于Java的分布式服务治理框架Dubbo,之后它成为了国内该类开源项目的佼佼者,许多开发者对其表示青睐。同时,先后有不少公司在实践中基于Dubbo进行分布式系统架构,目前在GitHub上,它的fork、star数均已破万。Dubbo致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案,使得应用可通过高性能RPC实现服务的输出、输入功能和Spring框架无缝集成。Dubbo包含远程通讯、集群容错和自动发现三个核心部分。
停止维护:从2012年10月23日Dubbo 2.5.3发布后,在Dubbo开源将满一周年之际,阿里基本停止了对Dubbo的主要升级。只在之后的2013年和2014年更新过2次对Dubbo 2.4的维护版本,然后停止了所有维护工作。Dubbo对Srping的支持也停留在了Spring 2.5.6版本上。
死而复生:多年漫长的等待,随着微服务的火热兴起,在国内外开发者对阿里不再升级维护Dubbo的吐槽声中,阿里终于开始重新对Dubbo的升级和维护工作。在2017年9月7日,阿里发布了Dubbo的2.5.4版本,距离上一个版本2.5.3发布已经接近快5年时间了。在随后的几个月中,阿里Dubbo开发团队以差不多每月一版本的速度开始快速升级迭代,修补了Dubbo老版本多年来存在的诸多bug,并对Spring等组件的支持进行了全面升级。
2018年1月8日,Dubbo创始人之一梁飞在Dubbo交流群里透露了Dubbo 3.0正在动工的消息。Dubbo 3.0内核与Dubbo 2.0完全不同,但兼容Dubbo 2.0。Dubbo 3.0将以Streaming为内核,不再是Dubbo 时代的RPC,但是RPC会在Dubbo 3.0中变成远程Streaming对接的一种可选形态。从Dubbo新版本的路线规划上可以看出,新版本的Dubbo在原有服务治理的功能基础上,将全面拥抱微服务解决方案。
结论:当前由于RPC协议、注册中心元数据不匹配等问题,在面临微服务基础框架选型时Dubbo与Spring Cloud是只能二选一,这也是为什么大家总是拿Dubbo和Spring Cloud做对比的原因之一。Dubbo之后会积极寻求适配到Spring Cloud生态,比如作为Spring Cloud的二进制通信方案来发挥Dubbo的性能优势,或者Dubbo通过模块化以及对http的支持适配到Spring Cloud。
Spring Cloud NetFlix
基于美国Netflix公司开源的组件进行封装,提供了微服务一栈式的解决方案。
Spring Cloud alibaba
在Spring cloud netflix基础上封装了阿里巴巴的微服务解决方案。
Spring Cloud Spring
目前spring官方趋势正在逐渐吸收Netflix组件的精华,并在此基础进行二次封装优化,打造spring专有的解决方案
Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management
, service discovery
, circuit breakers, intelligent routing, micro-proxy, control bus
). Coordination of distributed systems leads to boiler plate patterns, and using Spring Cloud developers can quickly stand up services and applications that implement those patterns. -------[摘自官网]
eurekaserver、consul、nacos 服务注册中心组件
rabbion & openfeign 服务负载均衡 和 服务调用组件
hystrix & hystrix dashboard 服务断路器 和 服务监控组件
zuul、gateway 服务网关组件
config 统一配置中心组件
bus 消息总线组件
Spring Cloud is an umbrella(伞) project consisting of independent projects with, in principle, different release cadences. To manage the portfolio a BOM (Bill of Materials) is published with a curated set of dependencies on the individual project (see below). The release trains have names, not versions, to avoid confusion with the sub-projects. The names are an alphabetic sequence (so you can sort them chronologically) with names of London Tube stations (“Angel” is the first release, “Brixton” is the second). When point releases of the individual projects accumulate to a critical mass, or if there is a critical bug in one of them that needs to be available to everyone, the release train will push out “service releases” with names ending “.SRX”, where “X” is a number. —[摘自官网]
Spring Cloud Dalston, Edgware, Finchley, and Greenwich have all reached end of life status and are no longer supported.
<parent>
<groupId>org.springframework.bootgroupId>
<version>2.2.5.RELEASEversion>
<artifactId>spring-boot-starter-parentartifactId>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR6spring-cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
完成上述操作springboot与springcloud环境搭建完成接下来就是使用到具体的springcloud组件,在项目中引入具体的组件即可
所谓服务注册中心就是在整个的微服务架构中单独提出一个服务,这个服务不完成系统的任何的业务功能,仅仅用来完成对整个微服务系统的服务注册和服务发现,以及对服务健康状态的监控和管理功能。
springcloud支持的多种注册中心Eureka、Consul、Zookeeper、
以及阿里巴巴推出Nacos
。这些注册中心在本质上都是用来管理服务的注册和发现以及服务状态的检查的。
Eureka Server和Eureka Client。
1.创建项目并引入eureka server依赖及springboot web依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
2.编写配置application.properties
server.port=8761 #执行服务端口
spring.application.name=eurekaserver #指定服务名称 唯一标识
eureka.client.service-url.defaultZone=http://localhost:8761/eureka #指定服务注册中心的地址
3.开启Eureka Server,入口类加入注解
@SpringBootApplication
@EnableEurekaServer
public class Eurekaserver8761Application {
public static void main(String[] args) {
SpringApplication.run(Eurekaserver8761Application.class, args);
}
}
4.访问Eureka的服务注册页面
5.虽然能看到管理界面为什么项目启动控制台报错?
出现上述问题原因:eureka组件包含 eurekaserver 和 eurekaclient。server是一个服务注册中心,用来接受客户端的注册。client的特性会让当前启动的服务把自己作为eureka的客户端进行服务中心的注册,当项目启动时服务注册中心还没有创建好,所以找我不到服务的客户端组件就直接报错了,当启动成功服务注册中心创建好了,日后client也能进行注册,就不再报错啦!
6.关闭Eureka自己注册自己
server.port=8761
spring.application.name=eurekaserver
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.register-with-eureka=false #不再将自己同时作为客户端进行注册
eureka.client.fetch-registry=false #关闭作为客户端时从eureka server获取服务信息
7.再次启动,当前应用就是一个单纯Eureka Server,控制器也不再报错
开发Eureka Client
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
2.编写配置application.properties
server.port=8888 #服务端口号
spring.application.name=eurekaclient8888 #服务名称唯一标识
eureka.client.service-url.defaultZone=http://localhost:8761/eureka #eureka注册中心地址
3.开启eureka客户端加入注解
@SpringBootApplication
@EnableEurekaClient
public class Eurekaclient8888Application {
public static void main(String[] args) {
SpringApplication.run(Eurekaclient8888Application.class, args);
}
}
4.启动之前的8761的服务注册中心,在启动eureka客户端服务
1.自我保护机制
2.在eureka server端关闭自我保护机制
eureka.server.enable-self-preservation=false #关闭自我保护
eureka.server.eviction-interval-timer-in-ms=3000 #超时3s自动清除
3.微服务修改减短服务心跳的时间
eureka.instance.lease-expiration-duration-in-seconds=10 #用来修改eureka server默认接受心跳的最大时间 默认是90s
eureka.instance.lease-renewal-interval-in-seconds=5 #指定客户端多久向eureka server发送一次心跳 默认是30s
4.尽管如此关闭自我保护机制还是会出现警告
eureka 停止更新
2.Consul
安装consul
2.安装consul
开发consul 客户端即微服务
1.创建项目并引入consul客户端依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
2.编写properties配置
server.port=8889
spring.application.name=consulclient8889
spring.cloud.consul.host=localhost #注册consul服务的主机
spring.cloud.consul.port=8500 #注册consul服务的端口号
spring.cloud.consul.discovery.register-health-check=false #关闭consu了服务的健康检查[不推荐]
spring.cloud.consul.discovery.service-name=${spring.application.name} #指定注册的服务名称 默认就是应用名
在主启动类上面加上@EnableDiscoveryClient
,代表该服务可以被注册到注册中心
注意: 这个注解是springcloud推行的一个通用注解,除了eureka做注册中心外,使用别的注册中心都可以使用该注解代表被服务发现与注册到注册中心上
@SpringBootApplication
@EnableDiscoveryClient // 作用:通用服务注册客户端注解 代表 consult client zk client nacos client
public class ConsulClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulClientApplication.class,args);
}
}
3.启动服务查看consul界面服务信息
consul 开启健康监控检查
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
consul 关闭健康监控检查
server.port=8889
spring.application.name=consulclient8889
spring.cloud.consul.host=localhost #注册consul服务的主机
spring.cloud.consul.port=8500 #注册consul服务的端口号
spring.cloud.consul.discovery.register-health-check=false #关闭consu了服务的健康检查[不推荐]
spring.cloud.consul.discovery.service-name=${spring.application.name} #指定注册的服务名称 默认就是应用名
一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)
。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。一致性(C)
:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)可用性(A)
:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)分区容忍性(P)
,就是高可用性,一个节点崩了,并不影响其它的节点(100个节点,挂了几个,不影响服务,越多机器越好)接下来在整个微服务架构中,我们比较关心的就是服务间的服务改如何调用,有哪些调用方式?
1.创建两个服务并注册到consul注册中心中
2.在商品服务中提供服务方法
@RestController
@Slf4j
public class ProductController {
@Value("${server.port}")
private int port;
@GetMapping("/product/findAll")
public Map<String,Object> findAll(){
log.info("商品服务查询所有调用成功,当前服务端口:[{}]",port);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","服务调用成功,服务提供端口为: "+port);
map.put("status",true);
return map;
}
}
3.在用户服务中使用restTemplate进行调用
@RestController
@Slf4j
public class UserController {
@GetMapping("/user/findAll")
public String findAll(){
log.info("调用用户服务...");
//1.使用restTemplate调用商品服务
RestTemplate restTemplate = new RestTemplate();
String forObject = restTemplate.getForObject("http://localhost:9998/product/findAll",
String.class);
return forObject;
}
}
5.测试服务调用
浏览器访问用户服务 http://localhost:9999/user/findAll
6.总结
官方网址: https://github.com/Netflix/ribbon
1.Ribbon 服务调用
1.项目中引入依赖
eureka client
和 consul client
,无须引入依赖,因为在eureka,consul中默认集成了ribbon组件
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
2.查看consul client中依赖的ribbon
3.使用restTemplate + ribbon进行服务调用
使用discovery client 进行客户端调用
使用loadBalanceClient 进行客户端调用
使用@loadBalanced 进行客户端调用
3.1 使用discovery Client形式调用
@Autowired
private DiscoveryClient discoveryClient;
//获取服务列表
List<ServiceInstance> products = discoveryClient.getInstances("服务ID");
for (ServiceInstance product : products) {
log.info("服务主机:[{}]",product.getHost());
log.info("服务端口:[{}]",product.getPort());
log.info("服务地址:[{}]",product.getUri());
log.info("====================================");
}
3.2 使用loadBalance Client形式调用
@Autowired
private LoadBalancerClient loadBalancerClient;
//根据负载均衡策略选取某一个服务调用
ServiceInstance product = loadBalancerClient.choose("服务ID");
log.info("服务主机:[{}]",product.getHost());
log.info("服务端口:[{}]",product.getPort());
log.info("服务地址:[{}]",product.getUri());
3.3 使用@loadBalanced
//1.整合restTemplate + ribbon
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
//2.调用服务位置注入RestTemplate
@Autowired
private RestTemplate restTemplate;
//3.调用
String forObject = restTemplate.getForObject("http://服务ID/hello/hello?name=" + name, String.class);
2.Ribbon负载均衡策略
1.ribbon负载均衡算法
RoundRobinRule
轮训策略 按顺序循环选择 Server
RandomRule
随机策略 随机选择 Server
AvailabilityFilteringRule
可用过滤策略
`会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
WeightedResponseTimeRule
响应时间加权策略
`根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用
RoundRobinRule策略,等统计信息足够会切换到
RetryRule
重试策略
`先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。
BestAviableRule
最低并发策略
`会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
3.修改服务的默认负载均衡策略
products.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class);
0.说明
1.openFeign 服务调用
1.服务调用方法引入依赖OpenFeign依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
2.入口类加入注解开启OpenFeign支持
@SpringBootApplication
@EnableFeignClients
public class Users9999Application {
public static void main(String[] args) {
SpringApplication.run(Users9999Application.class, args);
}
}
3.创建一个客户端调用接口
//value属性用来指定:调用服务名称
@FeignClient("PRODUCTS")
public interface ProductClient {
@GetMapping("/product/findAll") //书写服务调用路径
String findAll();
}
4.使用feignClient客户端对象调用服务
//注入客户端对象
@Autowired
private ProductClient productClient;
@GetMapping("/user/findAllFeignClient")
public String findAllFeignClient(){
log.info("通过使用OpenFeign组件调用商品服务...");
String msg = productClient.findAll();
return msg;
}
5.访问并测试服务
2.调用服务并传参
.说明:服务和服务之间通信,不仅仅是调用,往往在调用过程中还伴随着参数传递,接下来重点来看看OpenFeign在调用服务时如何传递参数
GET方式调用服务传递参数
1.GET方式调用服务传递参数
// 1.商品服务中添加如下方法
@GetMapping("/product/findOne")
public Map<String,Object> findOne(String productId){
log.info("商品服务查询商品信息调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品信息的id:[{}]",productId);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("productId",productId);
return map;
}
//2.用户服务中在product客户端中声明方法
@FeignClient("PRODUCTS")
public interface ProductClient {
@GetMapping("/product/findOne")
String findOne(@RequestParam("productId") String productId);
}
//3.用户服务中调用并传递参数
//注入客户端对象
@Autowired
private ProductClient productClient;
@GetMapping("/user/findAllFeignClient")
public String findAllFeignClient(){
log.info("通过使用OpenFeign组件调用商品服务...");
String msg = productClient.findAll();
return msg;
}
测试访问
post方式调用服务传递参数
2.post方式调用服务传递参数
//1.商品服务加入post方式请求并接受name
@PostMapping("/product/save")
public Map<String,Object> save(String name){
log.info("商品服务保存商品调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品名称:[{}]",name);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("name",name);
return map;
}
//2.用户服务中在product客户端中声明方法
//value属性用来指定:调用服务名称
@FeignClient("PRODUCTS")
public interface ProductClient {
@PostMapping("/product/save")
String save(@RequestParam("name") String name);
}
//3.用户服务中调用并传递参数
@Autowired
private ProductClient productClient;
@GetMapping("/user/save")
public String save(String productName){
log.info("接收到的商品信息名称:[{}]",productName);
String save = productClient.save(productName);
log.info("调用成功返回结果: "+save);
return save;
}
测试访问
2.传递对象类型参数
//1.商品服务定义对象
@Data
public class Product {
private Integer id;
private String name;
private Date bir;
}
//2.商品服务定义接收对象的方法
@PostMapping("/product/saveProduct")
public Map<String,Object> saveProduct(@RequestBody Product product){
log.info("商品服务保存商品信息调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品名称:[{}]",product);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("product",product);
return map;
}
//3.将商品对象复制到用户服务中
//4.用户服务中在product客户端中声明方法
@FeignClient("PRODUCTS")
public interface ProductClient {
@PostMapping("/product/saveProduct")
String saveProduct(@RequestBody Product product);
}
// 5.在用户服务中调用保存商品信息服务
//注入客户端对象
@Autowired
private ProductClient productClient;
@GetMapping("/user/saveProduct")
public String saveProduct(Product product){
log.info("接收到的商品信息:[{}]",product);
String save = productClient.saveProduct(product);
log.info("调用成功返回结果: "+save);
return save;
}
测试
OpenFeign超时设置
默认情况下,openFiegn在进行服务调用时,要求服务提供方处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行,但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。
1.模拟超时
3.修改OpenFeign默认超时时间
feign.client.config.PRODUCTS.connectTimeout=5000 #配置指定服务连接超时
feign.client.config.PRODUCTS.readTimeout=5000 #配置指定服务等待超时
#feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时
#feign.client.config.default.readTimeout=5000 #配置所有服务等待超时
OpenFeign调用详细日志展示
0.说明
往往在服务调用时我们需要详细展示feign的日志,默认feign在调用是并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。
我们可以为feign客户端配置各自的logger.lever对象,告诉feign记录那些日志logger.lever有以下的几种值
NONE 不记录任何日志
BASIC 仅仅记录请求方法,url,响应状态代码及执行时间
HEADERS 记录Basic级别的基础上,记录请求和响应的header
FULL 记录请求和响应的header,body和元数据
1.开启日志展示
feign.client.config.PRODUCTS.loggerLevel=full #开启指定服务日志展示
#feign.client.config.default.loggerLevel=full #全局开启服务日志展示
logging.level.com.baizhi.feignclients=debug #指定feign调用客户端对象所在包,必须是debug级别
2.测试服务调用查看日志
0.说明
1.作用
hystrix 用来保护微服务系统 实现 服务降级 服务熔断
服务雪崩
服务降级
服务熔断
1.服务雪崩
服务熔断
服务熔断图示
服务降级说明
服务压力剧增的时候根据当前的业务情况及流量对一些服务和页面有策略的降级,以此缓解服务器的压力,以保证核心任务的进行。同时保证部分甚至大部分任务客户能得到正确的响应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。
服务降级: 关闭微服务系统中某些边缘服务 保证系统核心服务正常运行
12 淘宝 京东
删除订单 — 关闭订单 确认收货 ----> 服务繁忙,!!!
服务降级图示
1.共同点
2.异同点
3.总结
0.服务熔断的实现思路
1.项目中引入hystrix依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
2.开启断路器
@SpringBootApplication
@EnableCircuitBreaker //用来开启断路器
public class Products9998Application {
public static void main(String[] args) {
SpringApplication.run(Products9998Application.class, args);
}
}
3.使用HystrixCommand
注解实现断路
//服务熔断
@GetMapping("/product/break")
@HystrixCommand(fallbackMethod = "testBreakFall" )
public String testBreak(int id){
log.info("接收的商品id为: "+ id);
if(id<=0){
throw new RuntimeException("数据不合法!!!");
}
return "当前接收商品id: "+id;
}
public String testBreakFall(int id){
return "当前数据不合法: "+id;
}
4.访问测试
5.总结
6.断路器打开条件
A service failure in the lower level of services can cause cascading failure all the way up to the user. When calls to a particular service exceed circuitBreaker.requestVolumeThreshold
(default: 20 requests) and the failure percentage is greater than circuitBreaker.errorThresholdPercentage
(default: >50%) in a rolling window defined by metrics.rollingStats.timeInMilliseconds
(default: 10 seconds), the circuit opens and the call is not made. In cases of error and an open circuit, a fallback can be provided by the developer. --摘自官方
原文翻译之后,总结打开关闭的条件:
7.默认的服务FallBack处理方法
@GetMapping("/product/hystrix")
@HystrixCommand(fallbackMethod = "testHystrixFallBack") //通过HystrixCommand降级处理 指定出错的方法
public String testHystrix(String name) {
log.info("接收名称为: " + name);
int n = 1/0;
return "服务[" + port + "]响应成功,当前接收名称为:" + name;
}
//服务降级处理
public String testHystrixFallBack(String name) {
return port + "当前服务已经被降级处理!!!,接收名称为: "+name;
}
6.服务降级的实现
服务降级: 站在系统整体负荷角度 实现: 关闭系统中某些边缘服务 保证系统核心服务运行
Emps 核心服务 Depts 边缘服务
1.客户端openfeign + hystrix实现服务降级实现
2.开启openfeign支持服务降级
feign.hystrix.enabled=true #开启openfeign支持降级
3.在openfeign客户端中加如Hystrix
@FeignClient(value = "PRODUCTS",fallback = ProductFallBack.class)
public interface ProductClient {
@GetMapping("/product/hystrix")
String testHystrix(@RequestParam("name") String name);
}
4.开发fallback处理类
public class ProductFallBack implements ProductClient {
@Override
public String testHystrix(String name) {
return "我是客户端的Hystrix服务实现!!!";
}
}
Hystrix DashBoard 仪表盘
1.项目中引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
dependency>
2.入口类中开启hystrix dashboard
@SpringBootApplication
@EnableHystrixDashboard //开启监控面板
public class Hystrixdashboard9990Application {
public static void main(String[] args) {
SpringApplication.run(Hystrixdashboard9990Application.class, args);
}
}
3.启动hystrix dashboard应用
4.监控的项目中入口类中加入监控路径配置[新版本坑],并启动监控项目
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
5.通过监控界面监控
6.点击监控,一致loading,打开控制台发现报错[特别坑]
解决方案
新版本中springcloud将jquery版本升级为3.4.1,定位到monitor.ftlh文件中,js的写法如下:
$(window).load(function()
jquery 3.4.1已经废弃上面写法
修改方案 修改monitor.ftlh为如下调用方式:
$(window).on(“load”,function()
编译jar源文件,重新打包引入后,界面正常响应。
1.说明
网关统一服务入口,可方便实现对平台众多服务接口进行管控,对访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等。
网关 = 路由转发 + 过滤器
2.为什么需要网关
3.网关组件在微服务中架构
Zuul is the front door for all requests from devices and web sites to the backend of the Netflix streaming application. As an edge service application, Zuul is built to enable dynamic routing, monitoring, resiliency and security.
0.原文翻译
1.zuul版本说明
2.springcloud 官方集成zuul文档
This project provides a library for building an API Gateway on top of Spring MVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
0.原文翻译
1.特性
1.开发网关动态路由
0.翻译
1.创建项目引入网关依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
快捷方式配置路由
2.编写网关配置
spring:
application:
name: gateway
cloud:
consul:
host: localhost
port: 8500
gateway:
routes:
- id: user_route # 指定路由唯一标识
uri: http://localhost:9999/ # 指定路由服务的地址
predicates:
- Path=/user/** # 指定路由规则
- id: product_route
uri: http://localhost:9998/
predicates:
- Path=/product/**
server:
port: 8989
3.启动gateway网关项目
再次启动成功启动
4.测试网关路由转发
java方式配置路由
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_route", r -> r.path("/order/**")
.uri("http://localhost:9997"))
.build();
}
}
2.查看网关路由规则列表
1.说明
management:
endpoints:
web:
exposure:
include: "*" #开启所有web端点暴露
访问路由管理列表地址
1.说明
spring:
application:
name: gateway
cloud:
consul:
host: localhost
port: 8500
gateway:
routes:
- id: user_route
#uri: http://localhost:9999/
uri: lb://users # lb代表转发后台服务使用负载均衡,users代表服务注册中心上的服务名
predicates:
- Path=/user/**
- id: product_route
#uri: http://localhost:9998/
uri: lb://products # lb(loadbalance)代表负载均衡转发路由
predicates:
- Path=/product/**
discovery:
locator:
enabled: true #开启根据服务名动态获取路由
4.常用路由predicate(断言,验证)
1.Gateway支持多种方式的predicate
After=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] `指定日期之后的请求进行路由
Before=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] `指定日期之前的请求进行路由
Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie=username,chenyn `基于指定cookie的请求进行路由
Cookie=username,[A-Za-z0-9]+ 基于指定cookie的请求进行路由
curl http://localhost:8989/user/findAll --cookie “username=zhangsna”
Header=X-Request-Id, \d+ ``基于请求头中的指定属性的正则匹配路由(这里全是整数)
`curl http://localhost:8989/user/findAll -H “X-Request-Id:11”
Method=GET,POST `基于指定的请求方式请求进行路由
官方更多: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#the-cookie-route-predicate-factory
2.使用predicate
spring:
application:
name: gateway
cloud:
consul:
host: localhost
port: 8500
gateway:
routes:
- id: user_route
#uri: http://localhost:9999/
uri: lb://users
predicates:
- Path=/user/**
- After=2020-07-21T11:39:33.993+08:00[Asia/Shanghai]
- Cookie=username,[A-Za-z0-9]+
- Header=X-Request-Id, \d+
Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.
1.原文翻译
官网:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#gatewayfilter-factories
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由筛选器的作用域是特定路由。springcloudgateway包括许多内置的GatewayFilter工厂。
2.作用
2.使用内置过滤器
增加请求头的filter
增加请求参数的filterr
增加响应头filter
增加前缀的filter
去掉前缀的filter
3.使用自定义filter
@Configuration
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("进入自定义的filter");
if(exchange.getRequest().getQueryParams().get("username")!=null){
log.info("用户身份信息合法,放行请求继续执行!!!");
return chain.filter(exchange);
}
log.info("非法用户,拒绝访问!!!");
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
0.说明
https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.3.RELEASE/reference/html/#_spring_cloud_config_server
config(配置)又称为 统一配置中心顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护。
1.统一配置中心组件流程图
Config Server 开发
1.引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
2.开启统一配置中心服务
@SpringBootApplication
@EnableConfigServer
public class Configserver7878Application {
public static void main(String[] args) {
SpringApplication.run(Configserver7878Application.class, args);
}
}
3.修改配置文件
server.port=7878
spring.application.name=configserver
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
4.直接启动服务报错
6.复制仓库地址
7.在统一配置中心服务中修改配置文件指向远程仓库地址
spring.cloud.config.server.git.uri=https://github.com/chenyn-java/configservers.git 指定仓库的url
spring.cloud.config.server.git.default-label=master 指定访问的分支
#spring.cloud.config.server.git.username= 私有仓库访问用户名
#spring.cloud.config.server.git.password= 私有仓库访问密码
8.再次启动统一配置中心
Config Client 开发
@Component
public class UrlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
String url = new String(httpServletRequest.getRequestURI());
//只过滤/actuator/bus-refresh请求
if (!url.endsWith("/bus-refresh")) {
chain.doFilter(request, response);
return;
}
//获取原始的body
String body = readAsChars(httpServletRequest);
System.out.println("original body: "+ body);
//使用HttpServletRequest包装原始请求达到修改post请求中body内容的目的
CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest);
chain.doFilter(requestWrapper, response);
}
@Override
public void destroy() {
}
private class CustometRequestWrapper extends HttpServletRequestWrapper {
public CustometRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
byte[] bytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return byteArrayInputStream.read() == -1 ? true:false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
}
public static String readAsChars(HttpServletRequest request)
{
BufferedReader br = null;
StringBuilder sb = new StringBuilder("");
try
{
br = request.getReader();
String str;
while ((str = br.readLine()) != null)
{
sb.append(str);
}
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (null != br)
{
try
{
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return sb.toString();
}
}