关于 Spring For All

关于 Spring For All

Spring For All 的一切
最新动态

最新动态

Spring 5 会是咋样呢
Spring Boot

Spring Boot

快速构建并运行 Spring 应用程序
Spring Cloud

Spring Cloud

分布式系统的一套工具,可用于构建微服务
Spring Framework

Spring Framework

提供依赖注入、事务、Web应用、数据访问等模块
Spring Data

Spring Data

提供一致性数据访问模块
Spring Security

Spring Security

提供应用身份验证和授权支持
Spring Batch

Spring Batch

提供高容批处理操作模块
Spring AMQP

Spring AMQP

基于AMQP消息解决方案
Micro Service Arch.

Micro Service Arch.

微服务架构相关
开源项目及视频教程

开源项目及视频教程

做中国最好的 Spring 开源项目及视频教程
小马哥专栏

小马哥专栏

阿里技术布道者,专注 Spring Boot 及微服务

Spring Boot、Dubbo项目Mock测试踩坑与总结

itmuch.com 发表了文章 • 0 个评论 • 192 次浏览 • 2017-09-06 09:35 • 来自相关话题

本文是对Spring Boot、Dubbo项目进行Mock测试的总结与踩坑实录。

搜索了一圈,居然没发现类似的文章,莫非用Dubbo的朋友们都不Mock测试,或者有其他的办法测试吗?

简单总结了一下,希望对大家能有一定参考意义。如果有更好的测试方法,请联系我的邮箱eacdy0000@126.com ,帮忙告知一下。

背景

手上有个整合了Dubbo的Spring Boot应用,在应用中需要消费其他服务的API。由于我依赖的服务并不由我所在的项目组维护(对方可能接口中途会发生变化,甚至,有时候可能并未启动)。

集成测试成本略高,故而想办法Mock测试。以RemoteApi为例,这是一个远程的API。我这一侧(消费者)的代码如下:
$(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });@Service
public class MyApi {
@Reference
private RemoteApi remoteApi;
public String hold() {
return remoteApi.hold();
}
}由代码可知,MyApi调用了一个远程的API RemoteApi。

下面我们来mock测试。

整合powermock

经过调研,笔者选择powermock作为项目的mock工具。

1.加依赖:<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-classloading-xstream</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>2.测试启动类:@SpringBootApplication
public class ConsumerTest {
public static void main(String[] args) {
SpringApplication.run(ConsumerTest.class, args);
}
@Bean
public RemoteApi RemoteApi() {
RemoteApi remoteApi = PowerMockito.mock(RemoteApi.class);
PowerMockito.when(remoteApi.hold())
.thenAnswer(t -> "我是Mock的API。");
return remoteApi;
}
}由代码可知,我在这里mock了一个RemoteApi,当调用Mock的RemoteApi.hold()方法时,返回我是Mock的API。3.测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}4.执行单元测试,发现Mock并没有成功,Dubbo依然会尝试调用远程API,而并非笔者Mock的RemoteApi。

分析

Mock没有成功,为什么呢?我们不妨将测试类代码修改成如下:@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
private ApplicationContext applicationContext;
@Before
public void before() {
MyApi myApi = applicationContext.getBean(MyApi.class);
RemoteApi fromMyApi = myApi.getRemoteApi();
RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
System.out.println("MyApi中注入的RemoteApi是:" + fromMyApi);
System.out.println("Spring容器中注入的RemoteApi是:" + fromSpring);
}
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}从代码不难发现,我在执行单元测试之前,分别打印MyApi对象中注入的RemoteApi,以及Spring容器中的RemoteApi(@Bean所注解的方法,new出来的对象,必然在Spring容器中)。

执行后,打印结果如下:1.MyApi中注入的RemoteApi是:com.alibaba.dubbo.common.bytecode.proxy0@541afb85
2.Spring容器中注入的RemoteApi是:remoteApi由打印结果可知,MyApi中注入的RemoteApi和容器中的RemoteApi,压根不是一个实例。

由该结果,可以知道2点:

1.Dubbo的@Reference注解拿到的一个代理;
2.@Reference生成的代理并不在Spring容器中(如果Dubbo的Reference的代理也是容器中,那么容器中应该有2个RemoteApi实例,那么调用getBean()应当报错);

解决

原因我们知道了,要如何解决呢?答案很简单——如果我们在执行单元测试之前,将StoreApi中注入的RemoteApi换成Spring容器中的实例(即我们Mock的那个对象),那么问题就可以得到就解决。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
private ApplicationContext applicationContext;
@Before
public void before() {
MyApi myApi = applicationContext.getBean(MyApi.class);
RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
myApi.setRemoteApi(fromSpring);
}
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}再次执行,就会发现,现在已经可以正常Mock了。

源码分析

以上已经提供了解决方案。那么,@Reference注解究竟干了哪些事情呢?我们不妨分析一下。搜索@Reference注解被哪些地方使用,可找到以下代码:com.alibaba.dubbo.config.spring.AnnotationBean#postProcessBeforeInitialization 。以该代码是我们定位问题的入口,由此,我们可以定位到以下两个方法
com.alibaba.dubbo.config.ReferenceConfig#initcom.alibaba.dubbo.config.ReferenceConfig#createProxy
其中,

1.createProxy方法用于创建代理对象;

2.init方法用来判断是否已经初始化,如果没有初始化,就会调用createProxy创建代理对象。过程比较简单,不贴了。
 
了解Dubbo如何创建对象后,我们来看看Dubbo是如何将代理对象设置到MyApi的,如下图。

 
分析至此,大家应该能够了解原因了——

1.@Reference创建了一个代理;
2.Dubbo自身做了一些判断,如果发现没有初始化,就会创建一个代理;
3.在postProcessBeforeInitialization 方法中,从Spring容器中拿到MyApi对象,并将这个代理对象设到MyApi实例中。
 
Going Far

我们已经知道,是@Reference注解搞的鬼,除了以上解决方案,还可以弄一个类,转一下。

即:原调用链:

MyApi —> RemoteApi
改为:

MyApi —> 一个转换的类,啥都不干,用了@Service注解,在里面调用RemoteApi的方法 —> RemoteApi

WHATS MORE OVER

如果使用xml配置,不存在该问题,可以很简单地Mock。

配套代码

https://github.com/itmuch/spring-boot-dubbo-mock-sample
 
 
 
本文链接: http://www.itmuch.com/dubbo/sp ... mock/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
本文是对Spring Boot、Dubbo项目进行Mock测试的总结与踩坑实录。

搜索了一圈,居然没发现类似的文章,莫非用Dubbo的朋友们都不Mock测试,或者有其他的办法测试吗?

简单总结了一下,希望对大家能有一定参考意义。如果有更好的测试方法,请联系我的邮箱eacdy0000@126.com ,帮忙告知一下。

背景

手上有个整合了Dubbo的Spring Boot应用,在应用中需要消费其他服务的API。由于我依赖的服务并不由我所在的项目组维护(对方可能接口中途会发生变化,甚至,有时候可能并未启动)。

集成测试成本略高,故而想办法Mock测试。以RemoteApi为例,这是一个远程的API。我这一侧(消费者)的代码如下:
@Service
public class MyApi {
@Reference
private RemoteApi remoteApi;
public String hold() {
return remoteApi.hold();
}
}
由代码可知,MyApi调用了一个远程的API RemoteApi。

下面我们来mock测试。

整合powermock

经过调研,笔者选择powermock作为项目的mock工具。

1.加依赖:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-classloading-xstream</artifactId>
<version>1.6.6</version>
<scope>test</scope>
</dependency>
2.测试启动类:
@SpringBootApplication
public class ConsumerTest {
public static void main(String[] args) {
SpringApplication.run(ConsumerTest.class, args);
}
@Bean
public RemoteApi RemoteApi() {
RemoteApi remoteApi = PowerMockito.mock(RemoteApi.class);
PowerMockito.when(remoteApi.hold())
.thenAnswer(t -> "我是Mock的API。");
return remoteApi;
}
}
由代码可知,我在这里mock了一个RemoteApi,当调用Mock的RemoteApi.hold()方法时,返回
我是Mock的API。
3.测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}
4.执行单元测试,发现Mock并没有成功,Dubbo依然会尝试调用远程API,而并非笔者Mock的RemoteApi。

分析

Mock没有成功,为什么呢?我们不妨将测试类代码修改成如下:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
private ApplicationContext applicationContext;
@Before
public void before() {
MyApi myApi = applicationContext.getBean(MyApi.class);
RemoteApi fromMyApi = myApi.getRemoteApi();
RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
System.out.println("MyApi中注入的RemoteApi是:" + fromMyApi);
System.out.println("Spring容器中注入的RemoteApi是:" + fromSpring);
}
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}
从代码不难发现,我在执行单元测试之前,分别打印MyApi对象中注入的RemoteApi,以及Spring容器中的RemoteApi(@Bean所注解的方法,new出来的对象,必然在Spring容器中)。

执行后,打印结果如下:
1.MyApi中注入的RemoteApi是:com.alibaba.dubbo.common.bytecode.proxy0@541afb85
2.Spring容器中注入的RemoteApi是:remoteApi
由打印结果可知,MyApi中注入的RemoteApi和容器中的RemoteApi,压根不是一个实例。

由该结果,可以知道2点:

1.Dubbo的@Reference注解拿到的一个代理;
2.@Reference生成的代理并不在Spring容器中(如果Dubbo的Reference的代理也是容器中,那么容器中应该有2个RemoteApi实例,那么调用getBean()应当报错);

解决

原因我们知道了,要如何解决呢?答案很简单——如果我们在执行单元测试之前,将StoreApi中注入的RemoteApi换成Spring容器中的实例(即我们Mock的那个对象),那么问题就可以得到就解决。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
@Autowired
private ApplicationContext applicationContext;
@Before
public void before() {
MyApi myApi = applicationContext.getBean(MyApi.class);
RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
myApi.setRemoteApi(fromSpring);
}
@Autowired
public MyApi myApi;
@Test
public void hold() {
Assert.assertEquals("我是Mock的API。", this.myApi.hold());
}
}
再次执行,就会发现,现在已经可以正常Mock了。

源码分析

以上已经提供了解决方案。那么,@Reference注解究竟干了哪些事情呢?我们不妨分析一下。搜索@Reference注解被哪些地方使用,可找到以下代码:com.alibaba.dubbo.config.spring.AnnotationBean#postProcessBeforeInitialization 。以该代码是我们定位问题的入口,由此,我们可以定位到以下两个方法
  1. com.alibaba.dubbo.config.ReferenceConfig#init
    com.alibaba.dubbo.config.ReferenceConfig#createProxy

其中,

1.createProxy方法用于创建代理对象;

2.init方法用来判断是否已经初始化,如果没有初始化,就会调用createProxy创建代理对象。过程比较简单,不贴了。
 
了解Dubbo如何创建对象后,我们来看看Dubbo是如何将代理对象设置到MyApi的,如下图。

 
分析至此,大家应该能够了解原因了——

1.@Reference创建了一个代理;
2.Dubbo自身做了一些判断,如果发现没有初始化,就会创建一个代理;
3.在postProcessBeforeInitialization 方法中,从Spring容器中拿到MyApi对象,并将这个代理对象设到MyApi实例中。
 
Going Far

我们已经知道,是@Reference注解搞的鬼,除了以上解决方案,还可以弄一个类,转一下。

即:原调用链:

MyApi —> RemoteApi
改为:

MyApi —> 一个转换的类,啥都不干,用了@Service注解,在里面调用RemoteApi的方法 —> RemoteApi

WHATS MORE OVER

如果使用xml配置,不存在该问题,可以很简单地Mock。

配套代码

https://github.com/itmuch/spring-boot-dubbo-mock-sample
 
 
 
  • 本文链接: http://www.itmuch.com/dubbo/sp ... mock/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

Spring Cloud与Dubbo共存方案总结

itmuch.com 发表了文章 • 0 个评论 • 175 次浏览 • 2017-09-06 09:21 • 来自相关话题

一、背景

假设有一个遗留的Dubbo系统,现在想改用Spring Cloud。

由于遗留Dubbo系统比较庞大,短期之内无法完成技术栈的迁移。因此需要“分步走”,即:初期实现两者共存,后期逐步绞杀Dubbo应用,最终实现技术栈的统一。

p.s. 这里并没有贬低Dubbo的意思,仅是按照该场景讨论。


二、头脑风暴

架构迁移、技术栈更换、项目重构时的第一步往往不是“改造”,而是“停止修改”。基于这个原则,个人不太倾向于去立即大幅重构Dubbo应用原先的代码。原因有二:首先是原则问题,更重要的是时间成本、技术风险很难得到控制。

而,假如新编写的Spring Cloud应用去进行迁就,例如:
 
完全不动Dubbo遗留系统,使用RestTemplate或Feign编写Dubbo(DubboX)的RESTful API客户端代理 —> 有一定的实现复杂度、Dubbo接口改造成RESTful API后,消费方都需要再次修改(开始是代理,后来不用代理,因此有二次修改的问题)。索性将Spring Cloud应用也整合Dubbo—>存在改造不完整、技术栈不统一、无法约束开发人员用哪种方式API、额外的复杂度的问题(越多的组件、越多的环节意味着越多的坑)。

考虑到一般来讲,遗留系统的改造过程中一般都是新系统调用老系统,很少出现老系统大规模调用新系统的场景(至少我这边目前是这样^_^)。因此,笔者列出几种仅需少量的代码编写成本即可实现Spring Cloud与Dubbo短期/长期共存,并且侵入性较小,同时还允许我们改造遗留Dubbo系统的几种方案,算是抛砖引玉。期待朋友们提出更优雅、成本更小的方案。

三、亮代码

Sample1:借助Ribbon调用Dubbo应用。

优点:架构不依赖Eureka或其他服务注册组件,借助Ribbon去调用Dubbo微服务暴露的RESTful API;

缺点:如果Dubbo微服务较多时,均需手动配置,不适合新式的部署环境(例如Docker,因为每次部署IP/端口可能都不同)

Sample2:借助Sidecar

使用Sidecar,Dubbo微服务必须实现健康检查(对于Spring Boot程序即:添加spring-boot-starter-actuator依赖)。

优点:
这种方式下,Dubbo应用也可通过Sidecar调用Spring Cloud微服务的接口,Sidecar是连接Spring Cloud应用于Dubbo应用的桥梁。可以通过Sidecar传播Dubbo微服务的健康状态到Eureka Server。

缺点:
在于每个Dubbo微服务节点必须额外部署一个Sidecar应用。在Dubbo微服务调用Spring Cloud微服务时,增加了调用链的长度。(需使用Sidecar转发)

Sample3:借助Eureka实现整合

将Dubbo应用也注册到Eureka上。

优点:
没有多余的组件(除了Dubbo的注册中心ZK)没有什么局限

缺点:
对于非Spring Boot的应用,改造有一定的成本。

四、GOING FAR

本项目中几个Demo中,都是手动编码为Dubbo应用开放RESTful API的,实际迁移过程可以借助cglib或者lombok之类的工具,实现从Dubbo接口道RESTful API的转换。本仓库主要还是为大家提供思路,不做具体讨论。

五、配套代码
 
https://github.com/itmuch/spring-cloud-dubbo-togetherhttps://git.oschina.net/itmuch/spring-cloud-dubbo-together
 
 
 
本文链接: http://www.itmuch.com/spring-c ... ther/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
一、背景

假设有一个遗留的Dubbo系统,现在想改用Spring Cloud。

由于遗留Dubbo系统比较庞大,短期之内无法完成技术栈的迁移。因此需要“分步走”,即:初期实现两者共存,后期逐步绞杀Dubbo应用,最终实现技术栈的统一。

p.s. 这里并没有贬低Dubbo的意思,仅是按照该场景讨论。


二、头脑风暴

架构迁移、技术栈更换、项目重构时的第一步往往不是“改造”,而是“停止修改”。基于这个原则,个人不太倾向于去立即大幅重构Dubbo应用原先的代码。原因有二:首先是原则问题,更重要的是时间成本、技术风险很难得到控制。

而,假如新编写的Spring Cloud应用去进行迁就,例如:
 
  • 完全不动Dubbo遗留系统,使用RestTemplate或Feign编写Dubbo(DubboX)的RESTful API客户端代理 —> 有一定的实现复杂度、Dubbo接口改造成RESTful API后,消费方都需要再次修改(开始是代理,后来不用代理,因此有二次修改的问题)。
  • 索性将Spring Cloud应用也整合Dubbo—>存在改造不完整、技术栈不统一、无法约束开发人员用哪种方式API、额外的复杂度的问题(越多的组件、越多的环节意味着越多的坑)。


考虑到一般来讲,遗留系统的改造过程中一般都是新系统调用老系统,很少出现老系统大规模调用新系统的场景(至少我这边目前是这样^_^)。因此,笔者列出几种仅需少量的代码编写成本即可实现Spring Cloud与Dubbo短期/长期共存,并且侵入性较小,同时还允许我们改造遗留Dubbo系统的几种方案,算是抛砖引玉。期待朋友们提出更优雅、成本更小的方案。

三、亮代码

Sample1:借助Ribbon调用Dubbo应用。

优点:架构不依赖Eureka或其他服务注册组件,借助Ribbon去调用Dubbo微服务暴露的RESTful API;

缺点:如果Dubbo微服务较多时,均需手动配置,不适合新式的部署环境(例如Docker,因为每次部署IP/端口可能都不同)

Sample2:借助Sidecar

使用Sidecar,Dubbo微服务必须实现健康检查(对于Spring Boot程序即:添加spring-boot-starter-actuator依赖)。

优点:
  • 这种方式下,Dubbo应用也可通过Sidecar调用Spring Cloud微服务的接口,Sidecar是连接Spring Cloud应用于Dubbo应用的桥梁。
  • 可以通过Sidecar传播Dubbo微服务的健康状态到Eureka Server。


缺点:
  • 在于每个Dubbo微服务节点必须额外部署一个Sidecar应用。
  • 在Dubbo微服务调用Spring Cloud微服务时,增加了调用链的长度。(需使用Sidecar转发)


Sample3:借助Eureka实现整合

将Dubbo应用也注册到Eureka上。

优点:
  • 没有多余的组件(除了Dubbo的注册中心ZK)
  • 没有什么局限


缺点:
  • 对于非Spring Boot的应用,改造有一定的成本。


四、GOING FAR

本项目中几个Demo中,都是手动编码为Dubbo应用开放RESTful API的,实际迁移过程可以借助cglib或者lombok之类的工具,实现从Dubbo接口道RESTful API的转换。本仓库主要还是为大家提供思路,不做具体讨论。

五、配套代码
 

 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... ther/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

Spring Cloud Security系列教程一:入门

itmuch.com 发表了文章 • 0 个评论 • 214 次浏览 • 2017-09-05 11:16 • 来自相关话题

本篇有一定的学习曲线,建议先花一点时间了解一下前置知识:
Spring Security:http://docs.spring.io/spring-s ... ngle/OAuth2(重点),参考文档:http://www.ruanyifeng.com/blog ... .htmlSpring Security OAuth2,参考文档:http://docs.spring.io/spring-b ... auth2

熟悉以上前置知识后,我们进入Spring Cloud Security的学习。在本例中,我们来使用GitHub的账号和密码来登录我们编写的应用。

准备工作

(1) 前往https://github.com/settings/developers,点击“Register a new application”按钮,添加一个应用。点击按钮后,界面如下图所示。

(2) 点击“Register application”按钮,即可出现如下图的界面。

记住这边的Client ID以及Client Secret,后面有用。

至此,准备工作就完成了。

编码

在这里,我们正式进行编码。

(1) 创建项目,并添加以下依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>即:为应用添加spring-cloud-starter-oauth2、spring-cloud-starter-security两个依赖。

(2) 编写启动类:@SpringBootApplication
@RestController
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
@GetMapping("/welcome")
public String welcome() {
return "welcome";
}
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
@Component
@EnableOAuth2Sso // 实现基于OAuth2的单点登录,建议跟踪进代码阅读以下该注解的注释,很有用
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/**")
// 所有请求都得经过认证和授权
.authorizeRequests().anyRequest().authenticated()
.and().authorizeRequests().antMatchers("/","/anon").permitAll()
.and()
// 这里之所以要禁用csrf,是为了方便。
// 否则,退出链接必须要发送一个post请求,请求还得带csrf token
// 那样我还得写一个界面,发送post请求
.csrf().disable()
// 退出的URL是/logout
.logout().logoutUrl("/logout").permitAll()
// 退出成功后,跳转到/路径。
.logoutSuccessUrl("/");
}
}
}
如代码所示,在这里,我们使用@EnableOAuth2Sso 注解,启用了“基于OAuth2的单点登录”,做了一些安全配置;同时,还定义了两个端点,/welcome 端点返回“welcome”字符串,/user 端点返回当前登录用户的认证信息。

这里说明一下,@EnableOAuth2Sso注解。如果WebSecurityConfigurerAdapter类上注释了@EnableOAuth2Sso注解,那么将会添加身份验证过滤器和身份验证入口。
 
如果只有一个@EnableOAuth2Sso注解没有编写在WebSecurityConfigurerAdapter上,那么它将会为所有路径启用安全,并且会在基于HTTP Basic认证的安全链之前被添加。详见@EnableOAuth2Sso的注释。

(3) 编写配置文件
server:
port: 8080
security:
user:
password: user # 直接登录时的密码
ignored: /
sessions: never # session策略
oauth2:
sso:
loginPath: /login # 登录路径
client:
clientId: 你的clientId
clientSecret: 你的clientSecret
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false配置文件中的地址可参考该文档来配置:https://developer.github.com/v3/oauth/。

这样,代码就编写完成了。

测试

(1) 启动应用。

(2) 访问http://localhost:8080/login,将会跳转到GitHub,进行认证。

(3) 认证通过后,访问http://localhost:8080/user,可看到当前用户的信息。

(4) 访问http://localhost:8080/logout,可正常退出应用。

配套代码

(1) https://github.com/itmuch/spri ... ity-1

(2) http://git.oschina.net/itmuch/spring-cloud-security-samples/tree/master/security-1
 
 
 
本文链接: http://www.itmuch.com/spring-c ... ty-1/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
本篇有一定的学习曲线,建议先花一点时间了解一下前置知识:
  1. Spring Security:http://docs.spring.io/spring-s ... ngle/
  2. OAuth2(重点),参考文档:http://www.ruanyifeng.com/blog ... .html
  3. Spring Security OAuth2,参考文档:http://docs.spring.io/spring-b ... auth2


熟悉以上前置知识后,我们进入Spring Cloud Security的学习。在本例中,我们来使用GitHub的账号和密码来登录我们编写的应用。

准备工作

(1) 前往https://github.com/settings/developers,点击“Register a new application”按钮,添加一个应用。点击按钮后,界面如下图所示。

(2) 点击“Register application”按钮,即可出现如下图的界面。

记住这边的Client ID以及Client Secret,后面有用。

至此,准备工作就完成了。

编码

在这里,我们正式进行编码。

(1) 创建项目,并添加以下依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
即:为应用添加spring-cloud-starter-oauth2、spring-cloud-starter-security两个依赖。

(2) 编写启动类:
@SpringBootApplication
@RestController
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
@GetMapping("/welcome")
public String welcome() {
return "welcome";
}
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
@Component
@EnableOAuth2Sso // 实现基于OAuth2的单点登录,建议跟踪进代码阅读以下该注解的注释,很有用
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/**")
// 所有请求都得经过认证和授权
.authorizeRequests().anyRequest().authenticated()
.and().authorizeRequests().antMatchers("/","/anon").permitAll()
.and()
// 这里之所以要禁用csrf,是为了方便。
// 否则,退出链接必须要发送一个post请求,请求还得带csrf token
// 那样我还得写一个界面,发送post请求
.csrf().disable()
// 退出的URL是/logout
.logout().logoutUrl("/logout").permitAll()
// 退出成功后,跳转到/路径。
.logoutSuccessUrl("/");
}
}
}
如代码所示,在这里,我们使用@EnableOAuth2Sso 注解,启用了“基于OAuth2的单点登录”,做了一些安全配置;同时,还定义了两个端点,/welcome 端点返回“welcome”字符串,/user 端点返回当前登录用户的认证信息。

这里说明一下,@EnableOAuth2Sso注解。如果WebSecurityConfigurerAdapter类上注释了@EnableOAuth2Sso注解,那么将会添加身份验证过滤器和身份验证入口。
 
  • 如果只有一个@EnableOAuth2Sso注解没有编写在WebSecurityConfigurerAdapter上,那么它将会为所有路径启用安全,并且会在基于HTTP Basic认证的安全链之前被添加。
  • 详见@EnableOAuth2Sso的注释。


(3) 编写配置文件
server:
port: 8080
security:
user:
password: user # 直接登录时的密码
ignored: /
sessions: never # session策略
oauth2:
sso:
loginPath: /login # 登录路径
client:
clientId: 你的clientId
clientSecret: 你的clientSecret
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false
配置文件中的地址可参考该文档来配置:https://developer.github.com/v3/oauth/

这样,代码就编写完成了。

测试

(1) 启动应用。

(2) 访问http://localhost:8080/login,将会跳转到GitHub,进行认证。

(3) 认证通过后,访问http://localhost:8080/user,可看到当前用户的信息。

(4) 访问http://localhost:8080/logout,可正常退出应用。

配套代码

(1) https://github.com/itmuch/spri ... ity-1

(2) http://git.oschina.net/itmuch/spring-cloud-security-samples/tree/master/security-1
 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... ty-1/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

Spring Cloud内置的Zuul过滤器详解

itmuch.com 发表了文章 • 0 个评论 • 133 次浏览 • 2017-09-05 10:45 • 来自相关话题

Spring Cloud默认为Zuul编写并启用了一些过滤器,这些过滤器有什么作用呢?我们不妨按照@EnableZuulServer、@EnableZuulProxy两个注解进行展开,相信大家对这两个注解都不陌生(至少都见过吧)。如果觉得陌生也没有关系,可将@EnableZuulProxy简单理解为@EnableZuulServer的增强版。事实上,当Zuul与Eureka、Ribbon等组件配合使用时,@EnableZuulProxy是我们常用的注解。

在Spring Cloud的官方文档中,只说@EnableZuulServer是一个“空白”的Zuul,那么究竟空白在什么地方呢?与@EnableZuulProxy到底有什么区别呢?诸多问题,都将在本文找到答案。

在此之前,我们先理解什么是RequestContext:

RequestContext:用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储在上下文中。

@EnableZuulServer过滤器

一、pre类型过滤器

(1) ServletDetectionFilter:该过滤器用于检查请求是否通过Spring Dispatcher。检查后,通过isDispatcherServletRequest设置布尔值。

(2) FormBodyWrapperFilter:解析表单数据,并为请求重新编码。

(3) DebugFilter:顾名思义,调试用的过滤器,可以通过zuul.debug.request=true ,或在请求时,加上debug=true的参数,例如$ZUUL_HOST:ZUUL_PORT/path?debug=true 开启该过滤器。这样,该过滤器就会把RequestContext.setDebugRouting() 、RequestContext.setDebugRequest() 设为true。

二、route类型过滤器

SendForwardFilter:该过滤器使用Servlet RequestDispatcher转发请求,转发位置存储在RequestContext.getCurrentContext().get("forward.to") 中。可以将路由设置成:
zuul:
routes:
abc:
path: /abc/**
url: forward:/abc
然后访问$ZUUL_HOST:ZUUL_PORT/abc ,观察该过滤器的执行过程。

三、post类型过滤器

SendResponseFilter:将Zuul所代理的微服务的的响应写入当前响应。

四、error类型过滤器

SendErrorFilter:如果RequestContext.getThrowable() 不为null,那么默认就会转发到/error,也可以设置error.path属性修改默认的转发路径。

@EnableZuulProxy过滤器

如果使用注解@EnableZuulProxy,那么除上述过滤器之外,Spring Cloud还会安装以下过滤器:

一、pre类型过滤器

PreDecorationFilter:该过滤器根据提供的RouteLocator确定路由到的地址,以及怎样去路由。该路由器也可为后端请求设置各种代理相关的header。

二、route类型过滤器

(1) RibbonRoutingFilter:该过滤器使用Ribbon,Hystrix和可插拔的HTTP客户端发送请求。serviceId在RequestContext.getCurrentContext().get("serviceId") 中。该过滤器可使用不同的HTTP客户端,例如

Apache HttpClient:默认的HTTP客户端

Squareup OkHttpClient v3:如需使用该客户端,需保证com.squareup.okhttp3的依赖在classpath中,并设置ribbon.okhttp.enabled = true 。
Netflix Ribbon HTTP client:设置ribbon.restclient.enabled = true 即可启用该HTTP客户端。需要注意的是,该客户端有一定限制,例如不支持PATCH方法,另外,它有内置的重试机制。

(2) SimpleHostRoutingFilter:该过滤器通过Apache HttpClient向指定的URL发送请求。URL在RequestContext.getRouteHost() 中。
 
 
 
本文链接: http://www.itmuch.com/spring-c ... loud/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
Spring Cloud默认为Zuul编写并启用了一些过滤器,这些过滤器有什么作用呢?我们不妨按照@EnableZuulServer、@EnableZuulProxy两个注解进行展开,相信大家对这两个注解都不陌生(至少都见过吧)。如果觉得陌生也没有关系,可将@EnableZuulProxy简单理解为@EnableZuulServer的增强版。事实上,当Zuul与Eureka、Ribbon等组件配合使用时,@EnableZuulProxy是我们常用的注解。

在Spring Cloud的官方文档中,只说@EnableZuulServer是一个“空白”的Zuul,那么究竟空白在什么地方呢?与@EnableZuulProxy到底有什么区别呢?诸多问题,都将在本文找到答案。

在此之前,我们先理解什么是RequestContext:

RequestContext:用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储在上下文中。

@EnableZuulServer过滤器

一、pre类型过滤器

(1) ServletDetectionFilter:该过滤器用于检查请求是否通过Spring Dispatcher。检查后,通过isDispatcherServletRequest设置布尔值。

(2) FormBodyWrapperFilter:解析表单数据,并为请求重新编码。

(3) DebugFilter:顾名思义,调试用的过滤器,可以通过zuul.debug.request=true ,或在请求时,加上debug=true的参数,例如$ZUUL_HOST:ZUUL_PORT/path?debug=true 开启该过滤器。这样,该过滤器就会把RequestContext.setDebugRouting() 、RequestContext.setDebugRequest() 设为true。

二、route类型过滤器

SendForwardFilter:该过滤器使用Servlet RequestDispatcher转发请求,转发位置存储在RequestContext.getCurrentContext().get("forward.to") 中。可以将路由设置成:
zuul:
routes:
abc:
path: /abc/**
url: forward:/abc
然后访问$ZUUL_HOST:ZUUL_PORT/abc ,观察该过滤器的执行过程。

三、post类型过滤器

SendResponseFilter:将Zuul所代理的微服务的的响应写入当前响应。

四、error类型过滤器

SendErrorFilter:如果RequestContext.getThrowable() 不为null,那么默认就会转发到/error,也可以设置error.path属性修改默认的转发路径。

@EnableZuulProxy过滤器

如果使用注解@EnableZuulProxy,那么除上述过滤器之外,Spring Cloud还会安装以下过滤器:

一、pre类型过滤器

PreDecorationFilter:该过滤器根据提供的RouteLocator确定路由到的地址,以及怎样去路由。该路由器也可为后端请求设置各种代理相关的header。

二、route类型过滤器

(1) RibbonRoutingFilter:该过滤器使用Ribbon,Hystrix和可插拔的HTTP客户端发送请求。serviceId在RequestContext.getCurrentContext().get("serviceId") 中。该过滤器可使用不同的HTTP客户端,例如

Apache HttpClient:默认的HTTP客户端

Squareup OkHttpClient v3:如需使用该客户端,需保证com.squareup.okhttp3的依赖在classpath中,并设置ribbon.okhttp.enabled = true 。
Netflix Ribbon HTTP client:设置ribbon.restclient.enabled = true 即可启用该HTTP客户端。需要注意的是,该客户端有一定限制,例如不支持PATCH方法,另外,它有内置的重试机制。

(2) SimpleHostRoutingFilter:该过滤器通过Apache HttpClient向指定的URL发送请求。URL在RequestContext.getRouteHost() 中。
 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... loud/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

zuul 如何配置GBK编码,似乎不支持

回复

springbootx 发起了问题 • 1 人关注 • 0 个回复 • 122 次浏览 • 2017-09-04 22:10 • 来自相关话题

@feignclient注解的类无法注入

zxddeveloper 回复了问题 • 2 人关注 • 1 个回复 • 174 次浏览 • 2017-09-04 17:21 • 来自相关话题

Spring Cloud Zuul过滤器详解

itmuch.com 发表了文章 • 0 个评论 • 104 次浏览 • 2017-09-04 14:28 • 来自相关话题

阅读本文,您将了解:

Zuul过滤器类型与请求生命周期
如何编写Zuul过滤器
如何禁用Zuul过滤器
Spring Cloud为Zuul编写的过滤器及其功能。

过滤器是Zuul的核心组件,本节我们来详细讨论Zuul的过滤器。

过滤器类型与请求生命周期

Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

(4) ERROR:在其他阶段发生错误时执行该过滤器。

除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

Zuul请求的生命周期如图8-5所示,该图详细描述了各种类型的过滤器的执行顺序。

图8-5 Zuul请求的生命周期

编写Zuul过滤器

理解过滤器类型和请求生命周期后,我们来编写一个Zuul过滤器。编写Zuul的过滤器非常简单,我们只需继承抽象类ZuulFilter,然后实现几个抽象方法就可以了。

那么现在,我们来编写一个简单的Zuul过滤器,让该过滤器打印请求日志。

(1) 复制项目microservice-gateway-zuul,将ArtifactId修改为microservice-gateway-zuul-filter。

(2) 编写自定义Zuul过滤器
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;
}
}
由代码可知,自定义的Zuul Filter需实现以下几个方法:

filterType:返回过滤器的类型。有pre、route、post、error等几种取值,分别对应上文的几种过滤器。详细可以参考com.netflix.zuul.ZuulFilter.filterType() 中的注释。
filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
shouldFilter:返回一个boolean值来判断该过滤器是否要执行,true表示执行,false表示不执行。
run:过滤器的具体逻辑。本例中,我们让它打印了请求的HTTP方法以及请求的地址。

(2) 修改启动类,为启动类添加以下内容:@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}测试

(1) 启动microservice-discovery-eureka。

(2) 启动microservice-provider-user。

(3) 启动microservice-gateway-zuul-filter。

(4) 访问http://localhost:8040/microservice-provider-user/1 ,可获得类似如下的日志。[nio-8040-exec-6] c.i.c.s.filters.pre.PreRequestLogFilter : send GET request to http://localhost:8040//microse ... ser/1
说明我们编写的自定义Zuul过滤器被执行了。

禁用Zuul过滤器

Spring Cloud默认为Zuul编写并启用了一些过滤器,例如DebugFilter、FormBodyWrapperFilter、PreDecorationFilter等。这些过滤器都存放在spring-cloud-netflix-core这个Jar包的org.springframework.cloud.netflix.zuul.filters包中。

一些场景下,我们想要禁用掉部分过滤器,此时该怎么办呢?

答案非常简单,只需设置zuul.<SimpleClassName>.<filterType>.disable=true ,即可禁用SimpleClassName所对应的过滤器。以过滤器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter为例,只需设置zuul.SendResponseFilter.post.disable=true ,即可禁用该过滤器。

同理,如果想要禁用《编写Zuul过滤器》一节编写的过滤器,只需设置zuul.PreRequestLogFilter.pre.disable=true 即可。

TPS

(1) 相关代码com.netflix.zuul.ZuulFilter.disablePropertyName()、com.netflix.zuul.ZuulFilter.isFilterDisabled()、com.netflix.zuul.ZuulFilter.runFilter()。
 
 
 
本文链接: http://www.itmuch.com/spring-c ... lter/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
  查看全部
阅读本文,您将了解:

Zuul过滤器类型与请求生命周期
如何编写Zuul过滤器
如何禁用Zuul过滤器
Spring Cloud为Zuul编写的过滤器及其功能。

过滤器是Zuul的核心组件,本节我们来详细讨论Zuul的过滤器。

过滤器类型与请求生命周期

Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

(4) ERROR:在其他阶段发生错误时执行该过滤器。

除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

Zuul请求的生命周期如图8-5所示,该图详细描述了各种类型的过滤器的执行顺序。

图8-5 Zuul请求的生命周期

编写Zuul过滤器

理解过滤器类型和请求生命周期后,我们来编写一个Zuul过滤器。编写Zuul的过滤器非常简单,我们只需继承抽象类ZuulFilter,然后实现几个抽象方法就可以了。

那么现在,我们来编写一个简单的Zuul过滤器,让该过滤器打印请求日志。

(1) 复制项目microservice-gateway-zuul,将ArtifactId修改为microservice-gateway-zuul-filter。

(2) 编写自定义Zuul过滤器
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;
}
}
由代码可知,自定义的Zuul Filter需实现以下几个方法:

filterType:返回过滤器的类型。有pre、route、post、error等几种取值,分别对应上文的几种过滤器。详细可以参考com.netflix.zuul.ZuulFilter.filterType() 中的注释。
filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
shouldFilter:返回一个boolean值来判断该过滤器是否要执行,true表示执行,false表示不执行。
run:过滤器的具体逻辑。本例中,我们让它打印了请求的HTTP方法以及请求的地址。

(2) 修改启动类,为启动类添加以下内容:
@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}
测试

(1) 启动microservice-discovery-eureka。

(2) 启动microservice-provider-user。

(3) 启动microservice-gateway-zuul-filter。

(4) 访问http://localhost:8040/microservice-provider-user/1 ,可获得类似如下的日志。
[nio-8040-exec-6] c.i.c.s.filters.pre.PreRequestLogFilter  : send GET request to http://localhost:8040//microse ... ser/1
说明我们编写的自定义Zuul过滤器被执行了。

禁用Zuul过滤器

Spring Cloud默认为Zuul编写并启用了一些过滤器,例如DebugFilter、FormBodyWrapperFilter、PreDecorationFilter等。这些过滤器都存放在spring-cloud-netflix-core这个Jar包的org.springframework.cloud.netflix.zuul.filters包中。

一些场景下,我们想要禁用掉部分过滤器,此时该怎么办呢?

答案非常简单,只需设置zuul.<SimpleClassName>.<filterType>.disable=true ,即可禁用SimpleClassName所对应的过滤器。以过滤器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter为例,只需设置zuul.SendResponseFilter.post.disable=true ,即可禁用该过滤器。

同理,如果想要禁用《编写Zuul过滤器》一节编写的过滤器,只需设置zuul.PreRequestLogFilter.pre.disable=true 即可。

TPS

(1) 相关代码com.netflix.zuul.ZuulFilter.disablePropertyName()、com.netflix.zuul.ZuulFilter.isFilterDisabled()、com.netflix.zuul.ZuulFilter.runFilter()。
 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... lter/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

 

《Spring Cloud与Docker微服务架构实战》配套代码

itmuch.com 发表了文章 • 0 个评论 • 115 次浏览 • 2017-09-04 14:18 • 来自相关话题

不才写了本使用Spring Cloud玩转微服务架构的书,书名是《Spring Cloud与Docker微服务架构实战》 - 周立,已于2017-01-12交稿。不少朋友想先看看源码,现将代码放出。

本次放出的代码:

共计70+个DEMO
覆盖Eureka、Ribbon、Feign、Hystrix、Zuul、Spring Cloud Config、Spring Cloud Bus、Spring Cloud Sleuth、Docker、Docker Compose等。

1-11章代码地址:

https://github.com/itmuch/spri ... -code
http://git.oschina.net/itmuch/ ... -code

12-14章配套代码地址:

https://github.com/itmuch/spri ... ocker
http://git.oschina.net/itmuch/ ... ocker

书目录

详见: http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
本文链接: http://www.itmuch.com/advertis ... code/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
不才写了本使用Spring Cloud玩转微服务架构的书,书名是《Spring Cloud与Docker微服务架构实战》 - 周立,已于2017-01-12交稿。不少朋友想先看看源码,现将代码放出。

本次放出的代码:

共计70+个DEMO
覆盖Eureka、Ribbon、Feign、Hystrix、Zuul、Spring Cloud Config、Spring Cloud Bus、Spring Cloud Sleuth、Docker、Docker Compose等。

1-11章代码地址:

https://github.com/itmuch/spri ... -code
http://git.oschina.net/itmuch/ ... -code

12-14章配套代码地址:

https://github.com/itmuch/spri ... ocker
http://git.oschina.net/itmuch/ ... ocker

书目录

详见: http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
  • 本文链接: http://www.itmuch.com/advertis ... code/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

Spring Cloud各组件配置属性总结

itmuch.com 发表了文章 • 0 个评论 • 113 次浏览 • 2017-09-04 14:16 • 来自相关话题

我们知道,Spring Cloud是个工具集,整合了各种组件。有的组件Spring Cloud是拿来主义,有的组件Spring Cloud又进行了一些增强(例如Feign)。这就导致Spring Cloud配置的分散——部分在Spring Cloud官方文档中有所提及,另外一些配置则分散在各组件自身Wiki中。

这样的现状,为Spring Cloud初学者带来新的挑战。事实上,经常有朋友在使用Spring Cloud的时候,咨询我相关组件的配置。

经过本书的讲解,相信大家已经发现,Spring Cloud大部分问题都可以使用配置属性来解决。本节笔者将相关组件的配置的地址罗列出来,方便读者查阅与检索。

Spring Cloud的配置

Spring Cloud的所有组件配置都在其官方文档的附录,地址如下:

http://cloud.spring.io/spring- ... rties

原生配置

我们知道,Spring Cloud整合了很多的工具,例如Eureka、Ribbon、Feign等。这些组件自身也有一些配置项,如下。

(1) Eureka的配置

https://github.com/Netflix/eur ... ureka

(2) Ribbon的配置

https://github.com/Netflix/rib ... Guide

(3) Hystrix的配置

https://github.com/Netflix/Hys ... ation

(4) Turbine的配置

https://github.com/Netflix/Tur ... tion-(1.x) )

节选自《Spring Cloud与Docker实战微服务》

目录详见:http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
本文链接: http://www.itmuch.com/spring-c ... tion/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
我们知道,Spring Cloud是个工具集,整合了各种组件。有的组件Spring Cloud是拿来主义,有的组件Spring Cloud又进行了一些增强(例如Feign)。这就导致Spring Cloud配置的分散——部分在Spring Cloud官方文档中有所提及,另外一些配置则分散在各组件自身Wiki中。

这样的现状,为Spring Cloud初学者带来新的挑战。事实上,经常有朋友在使用Spring Cloud的时候,咨询我相关组件的配置。

经过本书的讲解,相信大家已经发现,Spring Cloud大部分问题都可以使用配置属性来解决。本节笔者将相关组件的配置的地址罗列出来,方便读者查阅与检索。

Spring Cloud的配置

Spring Cloud的所有组件配置都在其官方文档的附录,地址如下:

http://cloud.spring.io/spring- ... rties

原生配置

我们知道,Spring Cloud整合了很多的工具,例如Eureka、Ribbon、Feign等。这些组件自身也有一些配置项,如下。

(1) Eureka的配置

https://github.com/Netflix/eur ... ureka

(2) Ribbon的配置

https://github.com/Netflix/rib ... Guide

(3) Hystrix的配置

https://github.com/Netflix/Hys ... ation

(4) Turbine的配置

https://github.com/Netflix/Tur ... tion-(1.x) )

节选自《Spring Cloud与Docker实战微服务》

目录详见:http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... tion/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

理解Eureka的自我保护模式

itmuch.com 发表了文章 • 0 个评论 • 89 次浏览 • 2017-09-04 14:13 • 来自相关话题

本文我们来探讨Eureka的自我保护模式。自我保护模式是Eureka的重要特性。进入自我保护模式最直观的体现,是Eureka Server首页输出的警告,如图4-10所示。

图4-10 Eureka Server自我保护模式界面

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。

Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

在Spring Cloud中,可以使用eureka.server.enable-self-preservation = false 禁用自我保护模式。

拓展阅读

(1) Eureka官方关于自我保护模式的介绍:

https://github.com/Netflix/eur ... ation

(2) Eureka的FAQ,其中讲到了自我保护模式:

https://github.com/Netflix/eureka/wiki/FAQ

(3) Eureka与Zookeeper做服务发现的对比:

http://dockone.io/article/78

(4) Eureka不注销任何微服务的讨论:

http://stackoverflow.com/quest ... rvice

节选自《Spring Cloud与Docker实战微服务》

目录详见:http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
本文链接: http://www.itmuch.com/spring-c ... tion/版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。 查看全部
本文我们来探讨Eureka的自我保护模式。自我保护模式是Eureka的重要特性。进入自我保护模式最直观的体现,是Eureka Server首页输出的警告,如图4-10所示。

图4-10 Eureka Server自我保护模式界面

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。

Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

在Spring Cloud中,可以使用eureka.server.enable-self-preservation = false 禁用自我保护模式。

拓展阅读

(1) Eureka官方关于自我保护模式的介绍:

https://github.com/Netflix/eur ... ation

(2) Eureka的FAQ,其中讲到了自我保护模式:

https://github.com/Netflix/eureka/wiki/FAQ

(3) Eureka与Zookeeper做服务发现的对比:

http://dockone.io/article/78

(4) Eureka不注销任何微服务的讨论:

http://stackoverflow.com/quest ... rvice

节选自《Spring Cloud与Docker实战微服务》

目录详见:http://www.itmuch.com/advertisment/my-spring-book/
 
 
 
  • 本文链接: http://www.itmuch.com/spring-c ... tion/
  • 版权声明: 本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。