目录
4.应用监控与管理:actuator (可以放在任何一个springboot项目上)
5.客户端之间的调用feign(Feign 是对 Ribbon的封装,使用注解的方式,调用起来更简单。。。 也是主流的方式~)
6.客户端之间的调用Ribbon(Ribbon 是使用 restTemplate 进行调用,并进行客户端负载均衡。)
7.熔断器:hystrix(熔断只是作用在服务调用这一端,与feign一块使用)
一.SpringCloud简介
springcloud是微服务架构,他是由多个独立项目集合而成的,每个项目都是独立的,各自进行自己的迭代和版本发布。所以springcloud不方便使用版本号来管理,而是使用版本名。以避免和子项目版本号的冲突。
二.常用组成
- 注册中心:eureak(除了eureak springcloud还提供了Consul服务中心)
- 客户端:client
- 配置中心:config
- 应用监控与管理:actuator
- 客户端之间的调用:feign
- 客户端负载均衡:ribbon
- 熔断器:hystrix
- 服务网关: Zuul,Geteway
- 消息驱动:Stream
- 服务链路追踪:Zipkin
- 消息总线:Bus
- 批量任务:Task
- 授权认证:SpringSecurity
三.代码实现
1.注册中心Eureak
- 创建一个springboot项目,里面啥组件都不许需要添加,或者用默认的eureak组件,我是搭建了一空的架子,一个一个添
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 在启动类添加注解,来标识这是一个eureak服务
@EnableEurekaServer
- 配置yml,单机配置,后面有其他的会在下面添加
server:
port: 8010
spring:
application:
name: eureka-server
eureka:
#指定主机名称
instance:
hostname: 127.0.0.1
#server一定程度上也是client,互为client,
client:
#由于自己就是服务器,不需要注册到自己
register-with-eureka: false
#由于自己就是服务器,不需要从服务器获取注册信息
fetch-registry: false
#服务地址
service-url:
defaultZone: http://127.0.0.1:8010/eureka/
- 启动访问:http://localhost:8010,出现下面的界面说明基本eureak搭建好了
- 添加security账号认证,在pom中添加security包
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
- 在yml中加入
server:
port: 8010
spring:
application:
name: eureka-server
security:
basic:
enabled: true
user:
name: herbert
password: 123456
eureka:
#指定主机名称
instance:
hostname: 127.0.0.1
#server一定程度上也是client,互为client,
client:
#由于自己就是服务器,不需要注册到自己
register-with-eureka: false
#由于自己就是服务器,不需要从服务器获取注册信息
fetch-registry: false
#服务地址
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
- 加入一个CSRF禁用问题,这是一个坑,大坑,查资料了解到新版(Spring Cloud 2.0 以上)的security默认启用了csrf检验,要在eurekaServer端配置security的csrf检验为false
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
2.客户端client
- 创建一个springboot项目,里面啥组件都不许需要添加,或者用默认的eureak组件,我是搭建了一空的架子,一个一个添
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 在启动类添加以下注解标识客户端
@EnableEurekaClient
- 配置yml文件,现在连接的是没有security的服务端
server:
port: 8005
spring:
application:
name: client-student-one
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8010/eureka/
- 访问服务端eureak地址:http://localhost:8010,会出现下面红线的地方
- 配置yml文件,现在连接的是有security的服务端
server:
port: 8005
spring:
application:
name: client-student-one
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
- 客户端自此配置完成,可以多配置几个客户端,方便后期的学习,我这边还添加了
server:
port: 8006
spring:
application:
name: client-student-two
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
---------------------------------------------------------------------------------
server:
port: 8007
spring:
application:
name: client-student-three
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
3.配置中心Config
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 再启动类加入以下配置来标记是配置中心
@EnableConfigServer
- 配置yml,看下图我的几个配置
(1)application-client.yml
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
(2)application-eureka.yml
server:
port: 8010
spring:
application:
name: eureka-server
security:
basic:
enabled: true
user:
name: herbert
password: 123456
eureka:
instance:
hostname: 127.0.0.1
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
(3)application.yml
server:
port: 8102
spring:
cloud:
config:
server:
native:
search-locations: classpath:/server/
application:
name: eureka-config
profiles:
active: native
- eureka config-server就搭建完了,我们来测试一下,项目跑起来,输入:http://127.0.0.1:8102/server/client,如下图就搭建完了
- 或者输入:http://127.0.0.1:8102/server/application-client.yml出现以下内容,就说明搭建成功
- 下一步就是让客户端链接我们的服务中心,这里有一个大坑,就是springboot读取配置文件的顺序,这里要把application.yml改成bootstrap.yml , 我这里将application.yml改成了bootstrap.yml
(1)加入pom的包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(2)配置bootstrap.yml
spring:
cloud:
config:
label: server
profile: client
uri: http://localhost:8102
server:
port: 8007
将所有的springboot都修改了,连接一个config,修改完后可以将config-server中的配置加载在其他服务里面
4.应用监控与管理:actuator (可以放在任何一个springboot项目上)
- 新建一个springboot项目,查看一下
- 添加pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 修改yml端口为 8011
server:
port: 8011
management:
endpoints:
web:
exposure:
include: "*" #暴露所有端点 默认是info,health
- 启动访问 众多 REST 接口、远程 shell 和 JMX 获得
- 例如:访问健康值 http://localhost:8011/actuator/health
{"status":"UP"} 是健康状态
具体可以参考一下下面的接口
其他的具体操作可以参考:https://blog.csdn.net/wya1993/article/details/80540981
5.客户端之间的调用feign(Feign 是对 Ribbon的封装,使用注解的方式,调用起来更简单。。。 也是主流的方式~)
- 我们用客户端2通过feign调用客户端1的接口
在客户端2中引入pom
<!-- 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.1.3.RELEASE</version>
</dependency>
加入feign启动注解,表示我是一个feign客户端
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
调用客户1的注解,首先在客户端1写一个接口
客户端2开始调用这个接口了,通过接口调用,其中@FeigenClient里面的迷你必须是注册中心的名字,在http://localhost:8010/eureak服务中心查找
最后就是在controller调用这个接口,调用成功
访问这个接口调用成功
6.客户端之间的调用Ribbon(Ribbon 是使用 restTemplate 进行调用,并进行客户端负载均衡。)
- 我们还是用客户端2和客户端1做测试,首先在客户端1中写一个接口为
- 我们用客户端2通过ribbon调用客户端1的ribbon接口
1.在客户端2添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.我们初始化restTemplate (RestTemplate 是由 Spring Web 模块提供的工具类,与 SpringCloud 无关,是独立存在的因 SpringCloud 对 RestTemplate 进行了一定的扩展,所以 只注入实例化时被@LoadBalanced修饰的实例。)再启动类中加入
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
3.调用客户端1的ribbon接口
这就是ribbon的调用,个人觉得还不如用feign
7.熔断器:hystrix(熔断只是作用在服务调用这一端,与feign一块使用)
- 我们继续用客户端2调用客户端1的接口,使用feign+hystrix
1.现在客户端1写一个接口
2.在客户端2的yml中开启hystrix
feign:
hystrix:
enabled: true
3.因为hystrix和feign一块用,一个客户端不能出现两个接口连接同一个feign,所以我们在上面的feign上修改
修改FeignService接口
添加ProductClientFeignHystrix类
@Component
public class ProductClientFeignHystrix implements FeignService {
@Override
public String getFrignHystrix() {
return "这个接口错了,现在到了这个接口这就是hystrix";
}
@Override
public String getFeign() {
return "这个接口错了,现在到了这个接口这就是hystrix";
}
}
在FeignController添加api接口
测试:你先访问http://localhost:8006/feignHystrix 然后再把客户端1断开,你在访问自己可以试试两次结果
8.网关
zuul和getway的区别和选择
Spring Cloud Gateway 是 Spring Cloud 微服务平台的一个子项目,属于 Spring 开源社区,依赖名叫:spring-cloud-starter-gateway。它是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
Spring Cloud Gateway 的特征:
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 动态路由
- Predicates 和 Filters 作用于特定路由
- 集成 Hystrix 断路器
- 集成 Spring Cloud DiscoveryClient
- 易于编写的 Predicates 和 Filters
- 限流
- 路径重写
Zuul 是 Netflix 公司的开源项目,Spring Cloud 在 Netflix 项目中也已经集成了 Zuul,依赖名叫:spring-cloud-starter-netflix-zuul。它提供了一个框架,可以对过滤器进行动态的加载,编译,运行。过滤器之间没有直接的相互通信。他们是通过一个RequestContext的静态类来进行数据传递的。RequestContext类中有ThreadLocal变量来记录每个Request所需要传递的数据。过滤器是由Groovy写成。这些过滤器文件被放在Zuul Server上的特定目录下面。Zuul会定期轮询这些目录。修改过的过滤器会动态的加载到Zuul Server中以便于request使用。
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
- 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
- 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
- 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
- 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
- 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
- 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
- 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
作者:一个会写诗的程序员
链接:https://www.jianshu.com/p/e0434a421c03
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一、Zuul(俗称springcloud的看门狗)
1.新建一个springboot2.0x的空架子,在pom中引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
--------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RC2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.再启动类加入注解
@EnableZuulProxy
@EnableEurekaClient
3.配置yml文件
server:
port: 8017
eureka:
client:
serviceUrl:
defaultZone: http://herbert:[email protected]:8010/eureka/
spring:
application:
name: eureka-zuul
zuul:
routes:
api-a:
path: /api-one/**
serviceId: CLIENT-STUDENT-ONE
api-b:
path: /api-two/**
serviceId: CLIENT-STUDENT-TWO
4.现在基本的zuul就搭建起来了。把项目跑起来访问client1的feign接口
原来访问是:localhost:8005/feign
现在通过zuul访问:localhost:8017/api-one/feign 就可以了
5.写zuulFilter
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @Author:hemingzhu
* @date: 2019/11/23 10:27
* @Explanation:
*/
@Component
public class ZuulFiterTest extends ZuulFilter {
@Override
public String filterType() {
System.out.println("11111");
return "pre"; // 可以在请求被路由之前调用
}
@Override
public int filterOrder() {
// filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低
return 0;
}
@Override
public boolean shouldFilter() {
// 是否执行该过滤器,此处为true,说明需要过滤
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(("--->>> TokenFilter {},{}"+ request.getMethod()+"->"+request.getRequestURL().toString()));
String token = request.getParameter("token");// 获取请求的参数
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //对请求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
return null;
} else {
ctx.setSendZuulResponse(false); //不对其进行路由
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
return null;
}
}
}
过滤器这就成功了
6.回退机制fallback
package com.herbert.eureak.zuul.fallback;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author:hemingzhu
* @date: 2019/11/23 10:54
* @Explanation:
*/
@Component
public class ZuulFallBack implements FallbackProvider {
@Override
public String getRoute() {
return null; //服务id,可以用* 或者 null 代表所有服务都过滤
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK; //请求网关成功了,所以是ok
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("错误".getBytes("UTF-8")); //返回前端的内容
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); //设置头
return httpHeaders;
}
};
}
}
测试:我们将客户端挂了访问 ----》 localhost:8017/api-one/feign?token=1 (token主要是过filter的,如果不写将filter屏蔽了就好)
zuul我们就说到这里
二、getway
---------------------------------------------------后续慢慢更新------------------------------------------------------