首页 > 经验记录 > 框架

 

 

万字长文警告 !建议在首页看的,点击标题进入文章页查看,好看点。

 

本文章又名: 兼容 Spring 体系的 java 框架实现妙计

 

 

前排先提示一波:   读我这篇文章可以没看过 MyBatis 源码,但是 Spring 源码最好是要看过的,因为很多东西我不会解释,没看过 Spring 源码的估摸着会有些懵逼。

 

看完这篇文章的话,基本上可以自己手写兼容Spring体系的框架了。

本文章大概能解答这些问题:

如何使用Spring提供的扫描器?

MyBatis 注解生成 mapper 的流程是怎样的?

通过注解选择配置的启用与否是如何实现的?

如何注入自己的动态代理对象到Spring IOC容器 (重点)

在注入自定义 BeanDefinition 到Spring IOC容器中时,FactoryBean 起到个什么作用?

 

 

 

先看效果,再谈实现

首先,我写的这个demo项目里只有一个dependency

 

然后,我有俩Dao接口,都提供了一个query()方法

 

最后,这是启动类和启动配置:

 

这是运行后的控制台打印:

——-> SELECT name FROM user WHERE id = 1
——-> SELECT name FROM user WHERE id = 2

 

这里先说明一下,JDBC的流程我是没写的,因为这个不是重点。我生成的动态代理对象只是单纯的解析了一下@Select注解 然后将其中的value值打印了出来而已。

 

 

如何实现 ?

首先,肯定是要实现这么个效果: 将自己自定义对象注入到Spring IOC 容器中。

但是Spring IOC容器内部,保存具体Bean实例的是 singletonObjects 这个ConcurrentHashMap (我这默认只说单例Bean啊)。而向这玩意里边塞值的方式 Spring 是没有提供给我们的,他只能通过Spring扫描Resouce资源然后解析为BeanDefinition再才能从getBean时解析BeanDefinition实例化对象放入此单例缓存中。

既然我们自己没有方法可以直接往单例缓存中塞值,那我们可以采取一点迂回战术。

想要实现注入这种效果,根据Spring IOC的加载Bean、使用Bean流程来说,提供了好几个不同的钩子方法给你自己实现然后在他整个Bean生命周期、IOC容器生命周期的不同阶段调用。

比如:  BeanFactoryPostProcessor

BeanFactoryPostProcessor: bean工厂的bean属性处理容器,主要是可以实现该接口从而管理我们的bean工厂内所有的BeanDefinition数据,可以随心所欲的修改属性。

BeanFactoryPostProcessor 的机制就相当于给了我们在 bean 实例化之前最后一次修改 BeanDefinition 的机会,我们可以利用这个机会对 BeanDefinition 来进行一些额外的操作,比如更改某些 bean 的一些属性,给某些 Bean 增加一些其他的信息等等操作。

BeanFactoryPostProcessor提供给我们实现的方法 postProcessBeanFactory() 中可以得到当前beanFactory的对象,然后通过该对象的 registerSingleton() 方法就可以注册一个Bean。

使用起来也很简单,比如我这样,就成功的往Spring IOC 容器中注册了一个name为”123asd”的String对象:

 

但是,用 BeanFactoryPostProcessor 不好。因为他是专门为了给你进行BeanDefinition的修改而生的,比如说你想改某个框架,就可能用到此类。

到这个钩子调用的阶段,BeanDefinition早就扫描完了,还没开始实例化。你在这强行注入已经实例化的对象到 singletonObject 里,用是可以用,但是违反了相应规范,也指不定会出什么未知bug。

 

我们最好还是能够在Spring的扫描阶段来注册BeanDefinition,保持和 Spring IOC 容器流程一致化。所幸Spring提供了专门的钩子用来给你注册BeanDefinition。只要有这个钩子,那就没问题了。

因为只要能够注入自己的BeanDefinition,在使用name获取对应的Bean时,要是获取不到,它就会试图获取此name对应的BeanDefinition,他只要一找到这玩意,就会开始实例化进程然后将其实例化,最后放入缓存后返回给调用者。

Spring提供的用来注册BeanDefinition的钩子有两个,一个是 BeanDefinitionRegistryPostProcessor,一个是 ImportBeanDefinitionRegistrar

BeanDefinitionRegistryPostProcessor也实现了BeanFactoryPostProcessor ,不过在实现此接口的时候可以无视BeanFactoryPostProcessor 提供的 postProcessBeanFactory()这个方法,一个类做一个事情,实现了BeanDefinitionRegistryPostProcessor就只用关心如何如何注册BeanDefinition就够了。

 

BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar的主要区别在于其提供给程序员实现的方法不同。

BeanDefinitionRegistryPostProcessor提供的方法仅带有一个BeanDefinitionRegistry参数 , 用于给你注册Bean。

而ImportBeanDefinitionRegistrar在其基础上,还额外带了一个参数 : AnnotationMetadata,此参数可以获取指定注解的属性

 

一般来说BeanDefinitionRegistryPostProcessor用来解析外部资源配置,ImportBeanDefinitionRegistrar解析注解配置。

以MyBatis来说,对应上边这俩的实现就是 :

MapperScannerConfigurer (实现BeanDefinitionRegistryPostProcessor)

MapperScannerRegistrar (实现 ImportBeanDefinitionRegistrar)

 

 

 

好,既然钩子有了,那么是不是就可以开始进行实例化了呢?

 

错!

 

首先,得用上这些钩子。咳咳,这不是在说废话啊!!

毕竟是一个写框架的流程,不是说想用就可以用的,不是说什么一个@Compent注解加上就完事了的。而是需要将配置摆在这里,用户需要的时候才进行配置加载。

如果是XML体系,那没啥好说的,用户就直接在 Spring 的 applicationContext.xml 配置里加载需要的配置Bean就行了。

那么问题来了,要是用注解呢?

 

只要用过Spring体系的,肯定用过一些Enable开头的注解,一般长这样: @EnableXXX  

只要在某个配置类或者说启动类上加入这个注解,就会自动加载某些框架的配置。比如什么 @EnableHystrix、@EnableEurekaServer、@EnableWebSecurity、@EnableCaching、@EnableAsync等等等等奇奇怪怪的注解。

这又是什么操作?

这个问题很好解决,还是以MyBatis举例,只要看一下MyBatis源码就知道这套东西是个什么意思了。

MyBatis的注解配置,有一个必要的类注解叫MapperScan,它主要是来定义扫描哪些包的。然后在该注解中导入了配置类进行加载。

 

看16行高亮行,导入的配置即为我上边说的实现了ImportBeanDefinitionRegistrar 接口的MapperScannerRegistrar。

关于Spring 的注解扫描机制,在使用注解加载配置时,Spring会对所有配置类上的注解进行扫描,也会对注解头上修饰的注解进行递归扫描。

@Import 注解 ,就属于一个Spring提供的特殊注解,扫描到该注解引用的类时,就会自动加载该类资源(也就是装载进Spring IOC容器),  一般来说就是拿来导入配置的。当然,导入普通的Bean也不是不行,不过用@Import来导入Bean就属于画蛇添足了。

导入配置的这个阶段,处于Spring IOC容器的扫描阶段。

 

其实像是这种 @EnableXXX  的注解,你点进他源码看,肯定是用了@Import 的,比如,我也这么定义了一个注解。

看我之前最开始贴出来的实现效果。是不是发现我用的 MainConfig.java 启动配置中,在类上注解了一个@EnableSkypyb 

这个 @EnableSkypyb  的底层我是这么写的

他导入了 SkypybRegistrar.class 这个配置,而这个配置,就是重中之重了。

因为,我是完全看着 MyBatis源码 写出的这个配置类,流程可以说和 MyBatis 的加载 mapper 流程一模一样。只是相对于MyBatis而言,少了很多判定和处理而已。

 

 

现在配置现在也能动态导入了

那么就可以进入扫描、修改BeanDefinition、实例化代理对象的流程了

 

核心配置代码:

 

逻辑清晰,看起来应该不困难。

理所当然的,为了使用@Import 导入实现了ImportBeanDefinitionRegistrar接口。同时还实现了一个ResourceLoaderAware 接口,其实实现这个ResourceLoaderAware 接口没什么鸟用,因为我当时是一直注入失败,所以MyBatis源码里那套东西我都试了一遍,他实现了ResourceLoaderAware 接口我也照葫芦画瓢实现的,实际上后来我全写完了后把这玩意删了也不影响。

 

SkypybRegistrar内部处理流程 :

1、先从 AnnotationMetadata 类中得到了我 @EnableSkypyb 注解内的的信息,主要是想知道,我应该扫描哪些包。

2、然后创建出自己自定义的扫描器,将 ImportBeanDefinitionRegistrar 提供的注册机 BeanDefinitionRegistry 传入进去

3、最后开始扫描,将需要扫描的包名数组enableSkypybValues 传入 doScan() 方法,扫描之前会先注册下扫描过滤器

 

就看这么个流程,肯定有点懵逼,因为到底是如何扫描出BeanDefinition的,又是如何将BeanDefinition改成我们自己定义的代理对象的,这里都没体现出来。

这个就必须要进入我自定义的 ClassPathSkypybScanner 这个类里了,不过我不会一股脑给他把代码全贴出来,不然可能会让人有点懵逼。

我接下来将以方法的粒度逐个方法进行讲解。探秘Spring IOC扫描器,以及我说的: FactoryBean 究极奥义

 

ClassPathSkypybScanner 类声明:

继承了ClassPathBeanDefinitionScanner ,只有一个构造方法,使用的是父类的构造器。

第一个参数是注册机,没什么好说的,第二个参数为是否使用默认过滤器,我传入false表示不使用。

 

关于ClassPathBeanDefinitionScanner :

ClassPathBeanDefinitionScanner作用就是将指定包下的类通过一定规则过滤后 将Class 信息包装成 BeanDefinition 的形式注册到IOC容器中。

可以继承他覆盖其方法,从而设置自定义的扫描器。实际上MyBatis就是这么做的,当然,我也是。

过滤器用来过滤从指定包下面查找到的 Class ,如果能通过过滤器,那么这个class 就会被转换成BeanDefinition 注册到容器。

这里我把它默认的过滤器禁掉,等会用自己写的过滤器

 

在我自己定义的自定义扫描器里边,最核心的就是 doScan() 方法了

这个直接贴代码:

 

流程也很简单:

1、直接调用父类(ClassPathBeanDefinitionScanner) 的 doScan() 方法,将我传入的包名中的类都扫描出来

2、ClassPathBeanDefinitionScanner 提供的 doScan() 在扫描后,会自动注册进 beanDefinitionMap

3、我获取了doScan()返回值后只需getBeanDefinition() 进行修改,反正指向的是一个堆内存地址

4、至于如何修改,之后再说,因为第一步调用父类 doScan() 方法有坑

 

ClassPathBeanDefinitionScanner的 doScan() 方法,你要是没做任何处理,他一个类都不会给你扫出来

需要你重写父类的 isCandidateComponent() 方法,在他扫描阶段,他会一个个走进这方法判断。

看其class是不是一个候选组件。只有是一个候选组件,才会进入到下一步判断是否通过过滤器

 

这是我重写的isCandidateComponent() 。只要是一个接口,并且是单独的,就判断其为一个候选组件。

 

至于我注册的过滤器,其实就是无脑返回true。

但是过滤器这一块是有门道的,很重要的。

我这只是为了演示,才自定义了一个过滤器让其全部通过。

addIncludeFilter() 传入一个TypeFilter,任何一个TypeFilter返回true就代表可以通过

TypeFilter接口目前有AnnotationTypeFilter实现类(类是否有注解修饰)、RegexPatternTypeFilter(类名是否满足正则表达式)等。

可以方便的实现只扫描出符合条件的类,比如 Spring 扫描 @Compone 修饰的类,他就是用的 AnnotationTypeFilter

 

只要这样写,调用父类的 doScan()  方法就可以成功扫描出指定的BeanDefinition了

然后,我在扫描完毕后,使用了自己自定义的方法 processBeanDefinitions() 对该BeanDefinition集合进行处理。

 

 

 

 

对,就到最关键的时机了!!!

总所周知,Spring IOC生命周期扫描出BeanDefinition后会放入beanDefinitionMap之中。

然后在 getBean() 的时候,才进行Bean的实例化进程,将Bean进行实例化。

 

我们现在,已经将mapper接口扫描出来了,并由Spring给我们封装完毕,将 BeanDefinition 注册进了 beanDefinitionMap。

这个时候,只要你启动Spring上下文。

就会报错:  BeanInstantiationException: Failed to instantiate [xxx.xxx.Xxx]: Specified class is an interface

 

因为你扫描出的只是一个普通的接口哇 !

就算封装成了BeanDefinition,也改变不了这个BeanDefinition描述的是个接口的事实啊!

Spring表示,很疑惑啊!他无法实例化啊!

 

而且,我们的终极目的是,偷天换日,将BeanDefinition描述的接口,改为我们自己定义的动态代理对象

 

如何才能将 BeanDefinition 描述的接口,修改为代理对象?

 

要知道,我不可能为每个接口都写个单独的代理,鬼知道用户提供了多少个接口?

我得有个统一的封装来表示用户提供的所有接口,并且可以在 运行时生成动态代理

此对象,还得有个 Class ,因为我得使用 BeanDefinition 的 setBeanClass() 方法,才能将该 BeanDefinition 的描述修改成我自定义的接口。

 

这个时候,就轮到 FactoryBean 出场了

这是 FactoryBean 接口,Spring 对实现了此接口的Bean进行了特殊处理。

主要表现为:

1、FactoryBean本身是个Bean
2、试图获取FactoryBean时,得到的是 FactoryBean 的 getObject() 返回的对象
3、可以通过 “&”+”name” 得到FactoryBean 本体对象

 

了解 FactoryBean 的应该不少。毕竟一些面试官总喜欢问 BeanFactory 和 FactoryBean 有什么区别这种只要背就能背会的问题。

但是,真的有人知道怎么用,在什么场景用这玩意吗?

 

好,现在场景有了,那么怎么用呢?

 

这里就涉及到 FactoryBean 配合 BeanDefinition 的 究极奥义 了。

 

已得知条件:

1、BeanDefinition 表示对一个Bean的描述,可以在实例化之前自由的更改他。

2、FactoryBean 就是一个Bean,但是 Spring 对其做了特殊处理,试图获取时,获取的是 getObject() 返回的对象

从以上条件进行推断:是不是可以在 FactoryBean 里边返回我自定义的代理对象?

 

可是,所有的mapper都要用某一个 FactoryBean 来返回代理对象。这个 FactoryBean 要怎么设计?

我是不是可以这么设计?

 

我可以用一个构造方法来接收指定的 Class 对象,然后动态返回此 Class对象的代理对象。

比如说我可以在此代理里边实现JDBC的逻辑、或者什么远程服务调用的逻辑。

 

可是,就算是这么用了:

那尼玛我 getBean()的时候 Spring 给我试图实例化出来,他也实例化不了啊?

更别提什么调用 getObject() 方法了。

 

而且这个class对象,必须给我从构造方法传进去。因为就算Spring给你实例化完了FactoryBean,他在 getObject() 时,由于没有class对象,也会报错。

这其中可没什么钩子给你 setClass 。

 

那怎么搞?

看上边已知条件第一条:  BeanDefinition 表示对一个Bean的描述,可以在实例化之前自由的更改他

 

 

我在setBeanClass() 之前,可以使用

来得到这个Bean原来的名字。 比如我那个 OneTestDao 接口,通过此方法就可以得到 :com.skypyb.dao.OneTestDao

 

然后通过

从而设置此 BeanDefinition 描述的 Bean 的构造参数

 

最终将刚刚获得的类名传入之后,Spring 解析BeanDefinition时 ,就会知道这个BeanDefinition 描述的 Bean 有个构造方法,要传的值是: com.skypyb.dao.OneTestDao

从而 成功实例化我们自定义的 FactoryBean。

 

最后的结果,就是通过我们自定义 FactoryBean 的实例对象的 getObject() 方法返回了自定义的代理,翻到文章最顶上演示,如你所见。

成功实现了 偷天换日,将BeanDefinition描述的接口,改为我们自己定义的动态代理对象

 

 

而且我的整个流程,和 MyBatis 可以说是 一模一样

不信?  MyBatis 源码 : MapperScannerRegistrar.class  请

 

 

 

整个工程的完整代码地址:  github

阅读全文

说是这么说哈,实际上这个泳道活动图只是一个大概流程,上边写的组件也基本上都是接口 而不是具体实现 (具体实现这鬼画得出来啊)

对于Spring这么强的东西来说,内部实现比我画的复杂多了,我这也就是随便画画 在线丢人而已

 

不过,就这么个简单的图还是凝结了我好久的知识总结的心血来着,从很久以前自学Spring开始,到后来因为职业在工作中长期的使用;

再到后期翻人家博客、看看部分源码,长久日子下来可算是对Spring熟悉点了而不是单纯的做个调参侠,最近因为离职了,就想着画个图巩固一下,在增强记忆的同时还可以练练好久没动用的UML技术。

结果为了画这个图,为了避免画错,又找了挺多资料学习 (其实到现在我也不确定我画的这个流程有没有问题,不过就算有应该也不会偏差多少)

越了解的深就越觉得Spring精妙,不愧是你.jpg

 

避免看不清图片,在这奉上链接,点开就能看:

http://assets.processon.com/chart_image/5d463499e4b07c4cf3012bd4.png

 

使用的画图工具为 processon ,是个网址

什么都好,就是不充钱只能画⑨个图,到现在为止我已经删掉好几个以前画的图了。

 

阅读全文

说是简单,tm那是找到解决方案之后才简单。

可能是我用的SpringCloud版本太新了,自己配zipkin server把我给配吐了。

又是版本冲突、又是注册不进去Eureka、又是访问ui报错、又是找不到ObjectProvider.orderedStream()方法的,我从百度搜到必应搜到google搜到Stack Overflow,整了一个上午。

我就想着,这玩意怎么能这么恶心呢?感觉像写maven的时候狂报xml错误那种感觉。

 

后来怼到zipkin官网 zipkin.io 上去,卧槽,快速入门里有这么句话:

If you have Java 8 or higher installed, the quickest way to get started is to fetch the latest release as a self-contained executable jar:

 

 

我佛了。然后又查了些关于高版本资料,关于 Zipkin 的服务端,在使用 Spring Boot 2.x 版本后,官方就不推荐自行定制编译了,反而是直接提供了编译好的 jar 包来给我们使用,这是官方提供的演示: https://github.com/openzipkin/docker-zipkin

白搞了半天,原来只要自己去下他的jar包然后启动这个服务就行了,至于如何将微服务跟踪的数据存到mysql/kafka/es 里边去,也可以直接用启动参数指定。

那我还自己配个蛇皮的zipkin server。

 

可以去这个网址: https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/ 选择下载zipkin的各个不同版本的启动jar,我随便找了一个,启动很顺畅,进入 localhost:9411 可以访问ui。

那服务端就算这样配完了,全部流程: 下载 –> 使用 java -jar 命令运行下载的jar

 

服务端搞完后,就到客户端了。

微服务链路跟踪嘛,需要得到请求经过了哪些微服务,了解请求流转的具体参数,那么客户端自然就是一个个不同的微服务了。

 

以我之前的user微服务为例,配置都一样。

build.gradle中加上这一行:

本来应该是spring-cloud-starter-sleuth,spring-cloud-sleuth-zipkin这两个依赖,但是spring-cloud-starter-zipkin这个集成了前面那俩,和前面那俩依赖效果是一样的。

加完依赖后只需要指定zipkin服务地址就好了

我的applicayion.yml:

 

spring.zipkin.base-url 指定地址。zipkin端口号默认9411。

spring.sleuth.sampler.probability 设置采样百分比,采样百分比就是会按比例来跟踪/保存多少条请求数据,因为在线上运行的话那数据是相当庞大的。默认设置的是0.1,也就是百分之十,我这设置百分之百是为了本地调试。

 

然后就完事了

对,没问题。所有微服务开起来,Eureka、Zuul等等,当然还有Zipkin官方服务。网页请求几个API,再去 localhost:9411 刷新一下,就可以看到每一条请求的详细信息了。

 

实现微服务跟踪需要的步骤:

1、 下载官方推荐的jar包

2、运行起来

3、改下微服务,加个依赖加个配置

完事了,如果想要保存数据到es/mysql/kafka里分析,可以设置启动参数,具体可以去看官方、github

 

 

我的使用Gradle搭的SpringCloud Finchley.SR3 项目整体地址:

https://github.com/skypyb/codeDemo/tree/master/sc-demo-microservice

阅读全文

上篇文章主要是搭建了一下Zuul的服务,并且实现了Zuul过滤器的自定义需求。

里边讲到了,Zuul 已经集成了 Ribbon、Hystrix ; 而 Ribbon无需配置,会在请求路由时自动给你进行负载均衡。

但是在Zuul服务路由不到对应微服务时,是没有对应的回退机制的,还是得自己手动写一下。

 

实现路由失败回退机制,首先需要继承 FallbackProvider 类,实现其getRoute() 和 fallbackResponse() 方法。

getRoute方法是指定你需要回退的线路,返回一个服务名就行。它会在路由该服务发生故障时调用 fallbackResponse 方法从而返回你一个 ClientHttpResponse 对象。

 

我写的回退代码:

 

 

这个代码会在路由到sc-demo-microservice-user这个服务出错时触发,然后根据不同的异常给出不同的响应(我这就是变了个Status哈)

配上一个回退机制后,就让Zuul服务更加完善健壮了,这样子,就算路由的服务挂掉了,我也能返回个东西让前端显示,也能做一些其余的抢救操作。

 

Zuul这个服务到这,其实也就差不多了,该有的都有,虽说也有Zuul这个服务挂掉的可能,但是Zuul也是可以集群的呀,前端可以访问一个负载均衡的服务器(比如Nginx),让他来转发请求到集群Zuul服务上边,这样子,整个微服务架构就非常健壮了(像这种Nginx服务一般不会挂哈,就一个纯走流量的服务器都能挂了,那你的流量是有多猛   除非硬件问题,那没辙)

 

咳咳, 说起来Zuul服务搭是搭好了,但是你就做一个服务路由+负载均衡,那也不是个事啊,我客户端那边请求服务,我要是需要多个数据,我是不是要发多个请求?

每个请求都要走–>zuul–>service 这一套流程,是不是不大好,毕竟我发多个请求来请求多个服务,总会有性能损耗。

那要是我就发一个请求到Zuul Server ,然后由Zuul 来帮我聚合请求多个微服务,包装成一个数据返回响应给我好不好鸭,那当然是极好的。这些微服务所在的服务器八成是在一个局域网里(除非你买的实例所在地区不同甚至供应商都不同哈) 那个网络传输速度,快的飞起来。

 

这个是我写请求聚合的代码:

 

我这就请求了zuul一个服务,然后就全权交给zuul给我把我要的数据都拿到了,组合成一个数据响应给我,是不是非常nice。我客户端的请求代码编写,也变得简单了。

 

到现在为止,整个Zuul架构就差不多了。自定义过滤器、回退、请求聚合、我还提供了一个zuul的集群高可用方案。基本上可以满足大多生产需求。

我的项目地址:Github

阅读全文

 

微服务架构有一个问题,那就是客户端如何访问各个微服务。

总不能在客户端APP/HTML写很多个不同的地址来请求吧?这样维护及其困难、开发不易。

这时候就需要一个网关,客户端的请求都发给这个网关, 然后由他来给你路由到别的微服务里边。

netflix 就开源了一个微服务网关:Zuul ,可以和 SpringCloudNetflix 全家桶(Eureka、Ribbon、Hystrix等)完美集成。

 

废话不多说.代码撸起来。

首先,项目创建,当然,创建的还是我那个Gradle搭的SpringCloudFinchley项目的子模块。

这回不用加什么依赖,只需要一个Zuul的依赖和Eureka Client的依赖就够了。

而且Zuul自己本身就集成了Ribbon和Hystrix,非常强大。

build.gradle:

 

然后在Application启动类上边加上@EnableZuulProxy 用以声明zuul代理

最主要的: 配置类yml文件

既然这个 zuul 是提供一个服务路由的功能,又已知 zuul 完美的集成了 Eureka。可以从Eureka Server中得到所有注册进去的服务。

那么,最需要进行配置的。自然是访问路径所对应的服务啦, 在zuul这个服务的配置文件里,专门有个地方给你写这个路由规则( zuul.routes )

我的配置文件:

 

虽然我这个配置文件里注释已经写得挺详细了,但还是简单的解释一下:

zuul.routes 这个属性树下面第一级节点,即我写的local-router、user-router 等,这一级只是让你写个路由规则的名字。 瞎写就行。

再下一级,大致可以分为两种:

1、路由到其余微服务的 指定好 service-id(即服务注册进Eureka的服务名) 和对应的访问路径就行了 ( 例如: 我用户微服务有个API请求路径是 user/{id},我现在就可以通过 ZUUL_ADDRESS:ZUUL_PORT/zuul_user/user/{id} 来访问我sc-demo-microservice-user中的API)。

 2、路由到本地由Zuul自己进行处理的 指定好客户端访问路径,和对应的本地API地址( forward: +本地API的地址) 就行

 

到这步,其实Zuul这个服务就搭建完毕了, 已经可以使用了。而且,不但可以帮你进行服务路由,还会用自带的Ribbon给你的请求进行负载均衡。

 

搭建完了,当然还不够,还能让这个 zuul 服务更加强大

zuul的路由功能是基于他自己编写的一大堆过滤器实现的,这里先简单说下他的过滤器类型

他主要有四大标准过滤器类型,这四种是最主要的

1、PRE  在请求被路由之前调用这个过滤器

2、ROUTING 这个过滤器将请求路由到微服务

3、POST 这个过滤器在路由到对应微服务之后再执行

4、ERROR 出错时进的

 

zuul 他自己有这几个过滤器的实现,当然,他写的我不动他,我可以自己额外写过滤器来实现自己的功能嘛。

zuul过滤器的实现非常简单,只要继承 ZuulFilter 这个类就完事了。

我写了个日志记录的过滤器:

这几个方法不多说,看下ZuulFilter源码的注释就知道具体是什么意思了。反正就是关于你写的过滤器的设置(类型、执行顺序、是否执行、具体逻辑)

我这过滤器很简单,就记录一下请求方法、地址,是个PRE类型的过滤器,会在路由之前调用。过滤器能干的事情很多,认证啊、加密啊啥的都行,看业务需求了。

 

 

阅读全文
EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00