简单spring cloud
微服务有哪些优势?
1.易于开发和维护
2.可以全自动部署
3.局部修改容易部署
4.技术栈不受限
微服务有哪些挑战?
1.运维成本比较高
2.分布式固有的复杂性
3.接口调整成本高
微服务设计原则?
1.单一职责原则
2.服务自治原则
3.轻量级通信原则
4.接口明确原则
常用的微服务有哪些,spring cloud作为其中一个微服务有哪些优势?
常见的微服务有spring cloud、dubbo等。它们的区别很多,比如说调用方式,spring cloud采用REST API方式,dubbo采用RPC;spring cloud几乎涉及到分布式组件各个方面,而dubbo仅仅实现了其中的服务治理模块。spring cloud优势在于它丰富的组件以及分布式解决方案集成springboot十分的方便快捷。
下面来谈谈springcloud丰富的组件和模块,下面只给出使用说明,概念性的说明省略。
1.服务发现组件
Eureka
application.yml如下配置
security:
basic:
enabled: true
user:
name: user
password: password123
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
EurekaApplication.java启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
2.服务注册(消费者和服务者都会注册服务)
application.yml如下配置
eureka:
client:
healthcheck: #健康检查 配合actuator
enabled: true
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka #权限验证
instance:
prefer-ip-address: true #服务可以ip调用
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
PS:eureka会将所有注册的服务按照application.name分组,availability zones标识集群节点数,status会显示各个实例状态(up表示可用),实例id可以用eureka.instance.instance-id来指定
3.服务消费ribbon
application.yml如何配置
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
ConsumerMovieRibbonApplicatio.java 启动类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class) //允许自定义ribbonclient
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) }) //保留默认的ribboclient
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
}
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
// @Autowired
// IClientConfig config;
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
//注意这边使用virtual hostname(provider applicationName),ribbon会根据virtual hostname找到对应几个服务,然后根据负载均衡算法选择一个服务
this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
PS:自定义负载均衡算法:默认是round,还有random、高可用、响应时间等
4.服务消费feign 声明式httpclient
ConsumerMovieFeignApplication.启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
新建服务调用方一致的方法,方法上注解基本同springmvc一致
@FeignClient("microservice-provider-user") //对应provider serviceId
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id); // 坑:1. @GetMapping不支持 2. @PathVariable得设置value 3.参数如果是复杂对象时,即使指定GET方法也失效
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(@RequestBody User user);
// 该请求不会成功,只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求。可能是我没找到相应的注解或使用方法错误
@RequestMapping(value = "/get-user", method = RequestMethod.GET)
public User getUser(User user);
}
5.eureka实现高可用
eureka配置
spring:
application:
name: EUREKA-HA
---
server:
port: 8761
spring:
profiles: peer1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
---
server:
port: 8762
spring:
profiles: peer2
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8763/eureka/
---
server:
port: 8763
spring:
profiles: peer3
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
PS:实现原理就是交叉注册,类似于主节点的备份,每个节点都在剩下的节点都有备份,从而实现集群
6.断路器hystrix
流程说明:
- ①.每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中.
- ②.执行execute()/queue做同步或异步调用.
- ③.是否开启请求缓存,如果开启了,并且如果缓存中对请求的响应可用,则此缓存响应将立即以“Observable”的形式返回。
- ④.判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤.
- ⑤.判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤.
- ⑥.调用HystrixCommand的run方法.运行依赖逻辑
- (1).依赖逻辑调用超时,进入步骤8.
- ⑦.判断逻辑是否调用成功
- (1).返回成功调用结果
- (2).调用出错,进入步骤8.
- ⑧.计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态.
- ⑨.getFallback()降级逻辑.
- 1.以下四种情况将触发getFallback调用:
- (1):run()方法抛出非HystrixBadRequestException异常。
- (2):run()方法调用超时
- (3):熔断器开启拦截调用
- (4):线程池/队列/信号量是否跑满
- 2.没有实现getFallback的Command将直接抛出异常
- 3.fallback降级逻辑调用成功直接返回
- 4.降级逻辑调用失败抛出异常
- 1.以下四种情况将触发getFallback调用:
- ⑩.返回执行成功结果
hystrix有两个隔离策略,thread和semaphore,默认是thread
thread 通过线程数量来限制并发请求数,可以提供额外的保护,但有一定的延迟,一般用于网络调用
semaphore 通过semaphore count来限制并发请求数,适用于无网络的高并发请求
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令执行超时时间
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。
理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"))
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
public User findByIdFallback(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
@RequestLine("GET /simple/{id}")
public User findById(@Param("id") Long id);
}
@Component
public class HystrixClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
明确问题1:进入断路器定义失败方法情况
(1):run()方法抛出非HystrixBadRequestException异常。
(2):run()方法调用超时
(3):熔断器开启拦截调用
(4):线程池/队列/信号量是否跑满
明确问题2:进入失败方法不一定短路器短路开关打开。
短路器短路开关有一定的条件,要满足再某个时间点失败次数达到一定数量。可以通过查看/hearth 来查看断路器状态。
断路器有三个状态,打开、关闭和半开。半开是指断路器打开后过一段时间它会尝试置为半开状态,即大部分请求还是短路,允许很少一部分流量请求,如果请求失败了,则断路器状态则变成打开,如果请求成功了,则断路器状态变成关闭。
7.服务网关zuul
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。
优点:
- 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证。可在微服务网关上进行认证。然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数。
实现方式如下 ZuulApplication.java 入口类
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
application.yml 配置类
zuul:
prefix: /api
routes:
aaa:
path: /user/**
serviceId: microservice-provider-user
legacy:
path: /**
serviceId: microservice-provider-user_old
PS:zuul.prefix表示所有走网关转发的路由必须要有api前缀,zuul默认支持serviceId/serviceName(除非设置zuul.ignoredServices=''),同时也可以设置path匹配规则,legacy表示当所有路由规则都不匹配才生效*
zuul同样支持断路器功能
配置一个FallbackProvider即可
@Component
public class MyFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "microservice-provider-user";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
String json = "{\"success\":false, \"msg\":\"error\"}";
return new ByteArrayInputStream(json.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
8.spring cloud config 分布式配置中心
spring cloud config作为配置中心优势?
- 集中管理
- 不同环境不同配置
- 运行期间动态调整配置
- 自动刷新
配置服务端 数据repo存放在git上,ConfigServer搭建如下
ConfigServerApplication 入口类
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
application.yml配置类
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/habib/{application} #通配符,匹配git habib这个目录下所有工程
server:
port: 8080
配置客户端 ConfigClient
bootstrap.yml
bootstrap.yml
spring:
cloud:
config:
uri: http://localhost:8080 #config server访问地址 如果config server配置登录校验,这边可以这样写 http://user:password123@localhost:8080,或者username和password写在下面
profile: dev #支持多环境 即对应application-dev.yml或者 application-dev.properties
label: master #默认就是master
application:
name: simple #对应https://gitee.com/habib/{application} 这个地址的application
PS:客户端加载顺序:bootstrap.yml -> 加载服务端配置文件 -> application.yml,所以要注意读取部分的配置要放在bootstrap.yml
9.spring cloud bus
整合了java的事件处理机制和消息中间件,可以简单的认为是一个轻量级的消息总线。使用时它默认需要在一个消息中间件基础上,比如说rabbitmq、kafka。
以rabbitmq为例子
bootstrap.yml 配置类
spring:
cloud:
config:
uri: http://localhost:8080
profile: dev
label: master
application:
name: simple
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
PS:config-server 和 config-client最好都添加上如上的消息配置。执行 http://config-server/bus/refresh 即可。 这个触发事件可以通过github webhook来触发。