MVC项目如何本地联调其他测试环境
source link: https://segmentfault.com/a/1190000041266750
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
MVC项目如何本地联调其他测试环境
目前公司还存在诸多前后端未分离项目(后面简称FM
项目),开发同学在本地只能联调alpha
环境,但是在项目转测转演之后,测试同学提的jira
单都是hwbeta
、gamma
环境的,因此开发同学为了复现问题场景需要在alpha
环境造单,或者麻烦测试同学造单,此过程非常低效。
我们以本地启动webagent
网关服务联调本地agentBuy-service
服务(Spring MVC
服务)为例分析流程。
agentBuy-service
服务是一个Spring MVC
服务,调用其他远程服务获取数据并注入到页面文件(ftl
)中,然后使用freemarker
引擎将ftl
编译成html
并返回给浏览器;- 浏览器加载
html
过程中会向static
静态资源服务请求静态资源; - 另外,页面还会向其他
web
服务发送请求,获取业务数据;
以上默认走的alpha
环境服务,如果想本地联调hwbeta
、gamma
环境的服务,就需要搞清楚其中webagent
是如何做路由转发的,远程服务是如何调用的
webagent
webagent
是采用SpringCloud Gateway
网关组件搭建的,如果想要路由转发到本地服务,需要更改config/routes.yml
路由配置文件,例如:
spring: cloud: gateway: httpclient: connectTimeout: 1000 #ms responseTimeout: 35s #Max Readtimeout pool: type: FIXED maxConnections: ${icec.http.pool.max:600} acquireTimeout: 10000 discovery.locator: enabled: false #default-filters: #- PreserveHostHeader #- AddCommonHtml forwarded.enabled: false routes: #- 开发环境需要下面这个路由 - id: dev-to-test predicates: - Path=/static/**, /passport/** uri: 'https://ec-alpha.casstime.com' - id: sellercenter-portal predicates: - Path=/agentBuy/seller/** # uri: 'lb://sellercenter-portal' # 默认走alpha环境 uri: 'http://localhost:10102' # 路由转发到本地sellercenter服务 - id: agentbuy-service predicates: - Path=/agentBuy/** # uri: 'lb://agentbuy-service' # 默认走alpha环境 uri: 'http://localhost:8001' # 路由转发到本地agentBuy-service服务
远程服务调用
Spring Cloud核心组件:Eureka
先来给大家说一个业务场景,假设咱们现在开发一个电商网站,agentBuy-service
需要向inquiry-service
获取用户地址,但是agentBuy-service
压根就不知道所依赖的服务在那台机器上,他就算想要发起一个请求,都不知道发送给谁,有心无力!
这时候,就轮到Spring Cloud Eureka
出场了。Eureka
是微服务架构中的注册中心,专门负责服务的注册与发现,其官方结构图如下:
Spring-Cloud Euraka
是Spring Cloud
集合中一个组件,它是对Euraka
的集成,用于服务注册和发现。Eureka
是Netflix
中的一个开源框架。它和 zookeeper
、Consul
一样,都是用于服务注册管理的,同样,Spring-Cloud
还集成了Zookeeper
和Consul
。
Eureka
由多个instance
(服务实例)组成,这些服务实例可以分为两种:Eureka Server
和Eureka Client
。为了便于理解,我们将Eureka client
再分为Service Provider
和Service Consumer
。
Eureka Server
为服务注册中心,向外暴露自己的地址,负责管理、记录服务提供者的信息,同时将符合要求的服务提供者地址列表返回服务消费者Service Provider
为服务提供者,在服务启动后,服务提供者向Eureka
注册自己的IP
、端口、提供服务等信息,并定时续约更新自己的状态等Service Consumer
为服务消费者,通过Eureka Server
发现得到所需服务的提供者地址信息,然后向服务提供者发起远程调用
agentBuy-service
是一个Spring MVC
服务,不需要向其他服务提供服务,也就不需要将自己注册到Eureka Server
,只需要作为Service Consumer
调用其他远程服务。agentBuy-service
配置成消费者服务如下:
(1)在pom
文件中添加Eureka-client
依赖
<dependency> <groupId>com.netflix.eureka</groupId> <artifactId>eureka-client</artifactId> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency>
(2)在eureka-client.properties
配置文件中增加相关配置信息,设置不需要注册到Eureka Server
,添加eureka.registration.enabled=false
和eureka.registerWithEureka=false
,详细如下:
// eureka-client.properties ###Eureka Client configuration for Sample Eureka Client # see the README in eureka-examples to see an overview of the example set up # note that for a purely client usage (e.g. only used to get information about other services, # there is no need for registration. This property applies to the singleton DiscoveryClient so # if you run a server that is both a service provider and also a service consumer, # then don't set this property to false. eureka.registration.enabled=false # 表示是否注册到Eureka Server,默认为true ## configuration related to reaching the eureka servers eureka.preferSameZone=true eureka.shouldUseDns=false eureka.registerWithEureka=false # 表示是否将自己注册到Eureka Server,默认为true eureka.serviceUrl.default=http://10.118.71.204:8761/eureka/ # 注册中心地址 eureka.appinfo.replicate.interval=10 eureka.vipAddress=agentbuy-service # 定义服务名变量 eureka.name=${eureka.vipAddress} # 服务名,SpringCloud中服务调用的依据 eureka.port=8001 # 服务端口 #eureka.ipAddr=192.168.29.170 #eureka.instanceId=${eureka.vipAddress}:${eureka.port} #eureka.hostname=${eureka.ipAddr} eureka.homePageUrlPath=/${eureka.vipAddress} #eureka.statusPageUrlPath=/${eureka.vipAddress}/status #eureka.healthCheckUrlPath=/${eureka.vipAddress}/health eureka.lease.renewalInterval=5 eureka.lease.duration=15 eureka.decoderName=JacksonJson
Spring Cloud核心组件:Feign
agentBuy-service
还集成Feign
实现依赖服务接口的定义。在Spring Cloud feign
的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon
时自行封装服务调用客户端的开发量。不需要自己写一大堆代码,跟其他服务建立网络连接,然后构造一个复杂的请求,接着发送请求过去,最后对返回的响应结果再写一大堆代码来处理。
这是上述流程翻译的代码片段,咱们一起来看看,体会一下这种绝望而无助的感受!!!
看完上面那一大段代码,有没有感到后背发凉、一身冷汗?实际上你进行服务间调用时,如果每次都手写代码,代码量比上面那段要多至少几倍,所以这个事儿压根儿就不是地球人能干的。
Feign
早已为我们提供好了优雅的解决方案。来看看如果用Feign
的话,你的订单服务调用库存服务的代码会变成啥样?
看完上面的代码什么感觉?是不是感觉整个世界都干净了,又找到了活下去的勇气!没有底层的建立连接、构造请求、解析响应的代码,直接就是用注解定义一个 FeignClient
接口,然后调用那个接口就可以了。人家Feign Client
会在底层根据你的注解,跟你指定的服务建立连接、构造请求、发起靕求、获取响应、解析响应,等等。这一系列脏活累活,人家Feign
全给你干了。
那么问题来了,Feign
是如何做到这么神奇的呢?很简单,Feign
的一个关键机制就是使用了动态代理。咱们一起来看看下面的图,结合图来分析:
- 首先,如果你对某个接口定义了
@FeignClient
注解,Feign
就会针对这个接口创建一个动态代理 - 接着你要是调用那个接口,本质就是会调用
Feign
创建的动态代理,这是核心中的核心 Feign
的动态代理会根据你在接口上的@RequestMapping
等注解,来动态构造出你要请求的服务的地址- 最后针对这个地址,发起请求、解析响应
agentBuy-service
是一个集成Spring MVC
搭建的web
项目,只要生成了bean
就可以使用@Autowired
注入实例,但agentBuy-service
要生成inquiry-service
的Feign
接口实例发起远程调用,不能使用@Autowired
注入实例,因为会报错找不到bean
“Could not autowire. No beans of 'InquiryClient' type found”
,所以项目中使用FeignClientBuilder
动态创建FeignClient
实例发起远程服务请求,缺陷是每次都需要手动创建,然后在作用域生命周期结束后销毁。
而Spring Boot
在启动类中使用了@SpringBootApplication
,其内部封装了@ComponentScan
,@EnableAutoConfiguration
,@SpringBootConfiguration
@ComponentScan
注解就是用来自动扫描被这些注解(@Service
,@Repository
,@Component
,@Controller
)标识的类,最终生成IOC
容器里的bean
@SpringBootConfiguration
用来声明当前类是一个配置类,可以通过@Bean
注解生成IOC
容器管理的bean
@EnableAutoConfiguration
是Spring Boot
实现自动化配置的核心注解,通过这个注解把spring
应用所需的bean注入容器中。@EnableAutoConfiguration
源码通过@Import
注入了一个ImportSelector
的实现类AutoConfigurationImportSelector
,这个ImportSelector
最终实现根据我们的配置,动态加载所需的bean
因此,使用@Autowired
注入实例
另外,如果生成bean
时指定FeignClientBuilder
类中configAgentUrl
属性(远程服务地址),那么可以跨Eureka
定位服务(不需要通过Eureka
定位)。Spring Boot
服务Feign
接口配置中没有指定服务调用的url
地址,而采用指定服务名的方式,导致依赖方服务如果想调用Feign
接口,必须要和被依赖方服务注册在同一个注册中心上
Spring Cloud核心组件:Ribbon
说完了Feign
,还没完。现在新的问题又来了,如果人家库存服务部署在了5台机器上,如下所示:
192.168.169:9000
192.168.170:9000
192.168.171:9000
192.168.172:9000
192.168.173:9000
这下麻烦了!人家Feign
怎么知道该请求哪台机器呢?
这时Spring Cloud Ribbon
就派上用场了。Ribbon
就是专门解决这个问题的。它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上
Ribbon
的负载均衡默认使用的最经典的Round Robin
轮询算法。这是啥?简单来说,就是如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器。。。以此类推。
此外,Ribbon
是和Feign
以及Eureka
紧密协作,完成工作的,具体如下:
- 首先
Ribbon
会从Eureka Client
里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口号。 - 然后
Ribbon
就可以使用默认的Round Robin
算法,从中选择一台机器 Feign
就会针对这台机器,构造并发起请求。
以订单服务远程调用库存服务为例,描述Eureka
、Ribbon
、Feign
三者之间的协同关系:
参考:一文读懂SpringCloud与Eureka,Feign,Ribbon,Hystrix,Zuul核心组件间的关系
agentBuy-service
服务本地联调其他环境
上面说到agentBuy-service
是一个集成Spring MVC
搭建的web
项目,服务调用的整体流程,如果需要在本地联调hwbeta、gamma
环境,需要注意一下几点:
(1)通过网关webagent
走本地agentBuy-service
(2)页面调用agentBuy-service
接口,agentBuy-service
默认调用alpha
环境远程服务,需要更改远程服务地址
因为agentBuy-service
采用FeignClientBuilder
动态创建FeignClient
实例去发起远程调用,生成bean
时指定FeignClientBuilder
中configAgentUrl
可以跨Eureka
调用服务,不需要通过Eureka Server
定位服务
(3)页面还可能调用其他web
服务接口,而web
服务由调用了其他远程服务,因此需要更改webagent
配置走本地web
服务,并且web
服务(Spring Boot
)的注册中心地址也需要更改。页面获取广告位数据是调用web-market
服务,以此为例描述下怎么更改配置:
# webagent/config/routes.yml - id: web-market predicates: - Path=/market/** #uri: 'lb://web-market' uri: 'http://localhost:9801' # 改成走本地web-market服务
设置项目启动的环境变量spring.profiles.active=hwbeta
,启用hwbeta
环境的配置
# bootstrap.yml server: port: 9801 contextPath: /market tomcat: protocolHeader: X-Forwarded-Proto encrypt.failOnError: false spring: application: name: web-market cloud: config: uri: http://config-center.alpha-intra.casstime.com/conf label: alpha management: port: 29801 contextPath: /hellgate security.enabled: false xmwlServiceUrl: https://xmwltest.casstime.com/edi/kaisi/getLogisticOrderTrace wenjuanxing.key: 8560a160-d42b-40d6-a97e-48b12cf51e8b --- spring: profiles: hwbeta cloud: config: uri: http://config-center.hwbeta-intra.casstime.com/conf # 改成去配置中心服务拉取hwbeta的配置,里面包含Eureka Server注册中心的地址 label: hwbeta --- spring: profiles: prod cloud: config: uri: http://config-server.intra.cassmall.com/conf label: master xmwlServiceUrl: https://xmwl.cassmall.com/edi/kaisi/getLogisticOrderTrace
本地联调其他环境的缺点
本地联调hwbeta、gamma
环境会导致环境被污染,以下面两个场景为例作下说明:
(1)如果本地打通了hwbeta
环境,本地agentBuy-service
能调通hwbeta
环境的远程服务quote-service
,当quote-service
发送一个消息推送,此时本地agentBuy-service
会接收到消息,消费此消息。假如这条消息表示供应商报价完成,agentBuy-service
接收到此消息后需要将shoppinglist
表中的询价单状态更改为报价完成,但是如果本地连接的是alpha
环境的数据库,这时是查找不到该询价单的,也就无法更改其报价状态,而该询价单正确状态应该是报价完成,实际却是未完成报价,导致hwbeta
环境数据被污染了。
(2)假如agentBuy-service
依赖了web-seller
和web-market
两个web
服务,web-seller
远程调用了A
服务,web-market
远程调用了B
服务,但是本地联调只更改了web-seller
指向A
服务的远程调用地址(hwbeta
),而没有更改web-market
指向B
服务远程地址(还是alpha
),导致页面提交接口入参数据有部分来自于alpha
,有些数据来自hwbeta
,因此写入的数据是错误的,hwbeta
环境也被污染了。
hwbeta
、gamma
环境类似于一个沙箱,几乎完全隔离,确保了测试的可靠性,一旦环境被污染,在该环境测试结果的可靠性就得不到保证,因此并不建议大家通过上述方式本地联调
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK