1.背景
因为最近要开展一个新项目,打算尝试用springcloud全家桶来做,从springcloud官网上看到如下版本:
在我写下这些文字的时候,springcloud已经发展到Hoxton(简称H)快照版本了,G版本也已经发布了一个稳定版本,由于springcloud是基于springboot的,从F版本后springcloud已全面基于springboot2.0以上的版本了,像之前的D、E版本都是基于springboot1.5左右的版本。所以我的技术选型是基于最保险的F版本,这样可以充份利用springboot2.0的特性,可能会有些坑,但自己应该可以填了。
2.概述
springcloud是微服务架构的集大成者,将一系列优秀的组件进行了整合。基于springboot构建,它提供的主要功能有:服务注册中心/服务注册/服务调用/负载均衡/断路器等,通过一些简单的注解,我们就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。因为目前只研究到这里,后面会进一步更新。
3.组件
3.1 Eureka
这里使用的注册中心是eureka,不过springcloud还提供了其他的注册中心,如consul,zookeeper等,看个人选择吧。Eureka可分成服务器端和客户端组件。
服务器端也可以成为服务注册中心,用于提供服务的注册和发现等功能;
客户端包含服务的消费者和生产者,用于向注册中心注册服务及获取调用服务的信息;
3.2 Ribbon
基于 HTTP 和 TCP客户端的负载均衡,解决的是服务发起方(在Eureka注册的服务)对被调用的服务的负载,即服务之间的负载均衡,需要与Spring的restTemplate结合使用。这里顺便提一下zuul也有负载均衡的功能,但主要针对外部请求对负载均衡。
3.3 Feign
Feign是在Ribbon的基础上改进的HTTP客户端,提供类似本地调用方式调用远程的服务,提高了的调用的简便性。
3.4 Zuul
Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。提供动态路由,监控,弹性,安全等边缘服务的框架。zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
1.验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
2.审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
3.动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
4.压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
5.负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
6.静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
7.多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
3.n xxxx(持续更新)
4.使用实例
4.1 简单场景
1. 启动注册中心,并且启动所有服务
2.服务调用者根据注册中心返回的信息进行服务调用。声明一下,这里没有明确的服务生产者和服务消费者,因为A服务在调用B服务的同时也在向C或D提供服务,所以明确一下概念,任何服务都可以是兼容这两种身份。
4.2 系统架构
这里我是使用了maven的聚合工程,主要包含discovery-center(服务中心)、microservice-user、microservice-movie、common-zuul模块, 将子模块通用的依赖全部写入最外部的pom文件中,主要包含一下依赖或组件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
这里springboot为什么选用2.0.5版本呢?因为springcloud Finchley.SR3版本只基于springboot 2.0.xx-——2.0.9999版本,2.0.5是我测试的能与springcloud Finchley.SR3版本兼容的很稳定的一个版本了。
4.3 discovery-center(服务中心)
这里只需要加入如下依赖即可:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
在启动类上加上@EnableEurekaServer注解即可,至于yml文件,如下配置:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
#以下两项配置的目的就是表明自己是server,做好自己server的事即可
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动完后通过http://localhost:8761/可以看到EurekaServer的监控页面
4.4 microservice-user
可以简称为A服务,pom文件中加入如下依赖,标明自己是一个客户端服务的身份:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在启动类上添加@EnableDiscoveryClient注解,也可以添加@EnableEurekaClient注解,因为用的是eureka服务中心,所以没太大影响,不必纠结。至于yml文件,如下配置:
server:
port: 7900
spring:
application:
name: microservice-user
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
新增测试测试类:
@RestController
@RequestMapping("condition")
public class ConditionController {
@GetMapping("status")
public boolean status(){
return true;
}
@GetMapping("self")
public String self(){
return "microService user";
}
}
启动main函数后,通过http://localhost:8761可以看到microservice-user节点注册;
4.5 microservice-movie
可称为B服务,pom文件中添加如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
启动类如下:创建restTemplate的bean
@EnableDiscoveryClient
@SpringBootApplication
public class MicroserviceMovieApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceMovieApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
yml文件如下:
server:
port: 7901
spring:
application:
name: microservice-movie
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
新增测试类:
@RestController
@RequestMapping("condition")
public class ConditionController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("status")
public boolean status(){
return true;
}
@GetMapping("self")
public String self(){
return "microService movie";
}
@GetMapping("test")
public String test(){
String test=restTemplate.getForEntity("http://microservice-user/condition/self",String.class).getBody();
return "This is "+test;
}
}
启动之后就可以在服务中心看到该节点,目前已有microservice-user,microservice-movie两个节点了。
4.7 common-zuul
pom.xml中加入如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
并在启动类上加上@EnableZuulProxy,表明开启zuul网关代理;
yml配置文件中:
server:
port: 8790
spring:
application:
name: common-zuul
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
microservice-user: /condition/self
启动之后就可以在服务中心看到该节点
5 服务调用
5.1 Ribbon调用
microservice-movie中已引入ribbon的依赖,且创建了restTemplate实例,
进行调用:http://localhost:7901/condition/test,则返回 This is microService user;说明调用成功。
如果想验证ribbon的负载均衡,则需要再开启一个microservice-user服务,端口为7902,在/condition/self接口的返回体中加上对应的端口。调用http://localhost:7901/condition/test,返回体This is microService user 7900,This is microService user 7902交替返回,所以ribbon的负载均衡是轮训机制。
5.2 Feign调用
在microservice-user中添加如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动来添加@EnableFeignClients注解,表示开启feign客户端。yml文件中添加如下配置:
feign:
hystrix:
enabled: true
测试类ConditionController中添加如下代码:
@Autowired
private RemoteClient remoteClient;
@GetMapping("feignTest")
public String feignTest(){
String test = remoteClient.self();
return "This is feign "+test;
}
并创建feign客户端的接口类:
@FeignClient("microservice-movie")
public interface RemoteClient {
@GetMapping("condition/self")
public String self();
}
这样所有的代码准备工作就完成了。
http://localhost:7900/condition/feignTest 调用远程服务之后返回:This is feign microService movie;表明成功。
其实fegin也有ribbon相同的负载均衡功能,再开启一个mocroservice-movie,端口为7903,在/condition/self返回体中加上端口号,http://localhost:7900/condition/feignTest 调用远程服务之后返回:This is feign microService movie 7901,This is feign microService movie 7903会交替出现。有兴趣可以试一试
5.3 zuul网关
开启两个microservice-user服务,http://localhost:8790/microservice-user/condition/self调用远程服务返回体:microService user 7902,microService user 7900交替出现,可以看到zuul进行了请求分发了。
再看一下zuul的请求过滤功能,新建类TokenFilter:
public class TokenFilter extends ZuulFilter {
//四种类型:pre,routing,error,post
//pre:主要用在路由映射的阶段是寻找路由映射表的
//routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调用
//error:一旦前面的过滤器出错了,会调用error过滤器。
//post:当routing,error运行完后才会调用该过滤器,是在最后阶段的
@Override
public String filterType() {
return "pre";
}
//自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
@Override
public int filterOrder() {
return 0;
}
//控制过滤器生效不生效,可以在里面写一串逻辑来控制
@Override
public boolean shouldFilter() {
return true;
}
//执行过滤逻辑
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getParameter("token");
if (token == null){
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
context.setResponseBody("unAuthrized");
return null;
}
return null;
}
}
并在启动类加载:
@Bean
public TokenFilter tokenFilter(){
return new TokenFilter();
}
再次请求http://localhost:8790/microservice-user/condition/self,返回“unAuthrized”
请求http://localhost:8790/microservice-user/condition/self?token=111,返回体:microService user 7902,microService user 7900交替出现。
问题
在使用springcloud F.SR3和springboot 2.0.5 版本结合中,遇到了很多问题,网上很多的案例都是以前较老的版本,如很多依赖已经找不到了,yml的配置属性也已过期。以上的代码是我经过2天结合springcloud Greenwich SR1版本和springboot 2.0.9版本的官方文档实践出来的,而且参考了一些大神网友的博客,如有侵权会立即更正。欢迎各位指正,正如韩愈一句诗说到:书山有路勤为径,苦海无涯苦作舟。 该文章会持续更新……