SpringCloud网关(Zuul)如何给多个微服务之间传递共享参数
目录
- 1、使用场景
- 解决方案1
- 解决方案2
- 2、代码实现
- 3、成果展现
- 4、总结
1、使用场景 因为最近项目需要国际化,但是以前国际化的语言切换是放置在未进行微服务化之前的一个独立的SpringBoot服务之中。
目前由于业务的需要和不同模块能够复用的要求目前已经拆分为如下服务:zuul网关服务、**dx服务(包含主要服务)、**ai(一些推荐的AI服务)、message(消息咨询服务)、forum(论坛版块服务)等等。
如果还是按照以前的的方式只切换 **dx服务 之中的语言;其他微服务是不知道当前的Request请求是什么语言体系的。
就意味**dx服务 是en语言,但是其他服务还是 zh(中文语言)。
因为此问题,我本人在项目开始进行国际化改版的时候,我就知道有此问题。 我就知道有此问题。 最开始就是有人不信;最后遇见问题了还是相信了!遇见问题了,总得使用相关的方案来解决此问题。
于是本人就是想到了有如下两种解决方案:
解决方案1
在网关服务(Zuul)之中,写一个统一的切换语言接口;在切换语言的使用同时调用其他服务的切换语言接口。这样实现起来最为简单。
具体如下图所示:
文章图片
此方法比较原始直接,但是书写代码比较多;想当与每个微服务都得提供一个修改语言体系的接口供 网关(Zuul)
解决方案2
【SpringCloud网关(Zuul)如何给多个微服务之间传递共享参数】在网关(Zuul)服务层进行语言切换时候获得切换语言的参数;使用 LocaleContextHolder 暂存当前已经切换语言的信息;
然后在Zuul的过滤器之中通过获得当前请求(Request)的参数后,把以前语言切换暂存参数值从LocaleContextHolder获得切换语言的参数值。然后把这个参数传递到其他微服务之中;其他微服务通过Request 即可获得最新切换的语言参数;然后做到顺利获取切换后的最新的语言信息。
具体如下图所示:
文章图片
针对方案一与方案二比较后;我选择使用的方案二。
2、代码实现
@Overridepublic Object run() {RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String reqUrl=request.getRequestURL().toString(); HttpSession session = request.getSession(); Map user=(Map) session.getAttribute("user"); if(!(reqUrl.contains("permissionUser/userLogin") || (reqUrl.contains("noAuth")) || reqUrl.contains("appapi"))) {String[] actProfile = env.getActiveProfiles(); String curProfile = actProfile[0]; if (StringUtils.startsWith(curProfile, "dev")) { //dev环境不做过滤return null; } //判断用户ID是否存在,不存在就跳转到登录界面if (!checkUserIsLogin(ctx, user)) {// System.out.println("session丢失: " + request.getSession().getId()); return null; }//放行ignoreUrls中配置的urlif(checkIsIgnoreUrl(request)) {addUserToZuulRequestHeader(user); return null; }//检查该url是否有权访问if (!checkIsUrlHasRight(ctx)) {return null; }//从会话之中获得当前登录用户的userId,判断用户是否被踢if (checkUserIsKickout(ctx, user)) {return null; }}//可以往后走了,把session放入request的header中addUserToZuulRequestHeader(user); //语言包setLanguageLocal(request); return null; } /*** 设置本地语言包* */ private void setLanguageLocal(HttpServletRequest request) {String lang = "zh"; if (StringUtils.isNotEmpty(request.getParameter("lang"))) {lang = request.getParameter("lang"); }else {lang=localeMessageSourceService.getCurrentLanguage(); }switch(lang) {case "zh":LocaleContextHolder.setLocale(Locale.CHINESE); break; case "en":LocaleContextHolder.setLocale(Locale.ENGLISH); break; case "fr":LocaleContextHolder.setLocale(Locale.FRENCH); break; }//此处为获得请求的参数 然后在请求的参数里面加入暂存的语言携带参数 langRequestContext ctx = RequestContext.getCurrentContext(); request.getParameterMap(); Map> requestQueryParams=ctx.getRequestQueryParams(); if(requestQueryParams==null) {requestQueryParams=new HashMap<>(); }//String langCode=request.getParameter("lang").toString(); //讲需要新增的参数添加进去,被调用的微服务可以直接获取,就像普通的一样;框架会直接注入进去ArrayList arrayList = new ArrayList<>(); arrayList.add(lang); requestQueryParams.put("lang", arrayList); ctx.setRequestQueryParams(requestQueryParams); FrameWorkConstant.MSG_INFO_EMPTY = localeMessageSourceService.getMessage("msg.info.empty"); FrameWorkConstant.MSG_INFO_SUCCESS = localeMessageSourceService.getMessage("msg.success.operate"); FrameWorkConstant.MSG_INFO_FAILED= localeMessageSourceService.getMessage("msg.error.unknown"); }
ChangeLanauageConfigurer
import java.util.Locale; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; /*** *@purpose:国际化启动配置类 *@since:2018年4月9日 ***/@Configurationpublic class ChangeLanauageConfigurer extends WebMvcConfigurerAdapter { /*** 描述 : 国际化配置类(会话区域解析器)**/ @Beanpublic SessionLocaleResolver localeResolver() {SessionLocaleResolver slr = new SessionLocaleResolver(); //System.out.println("aaaaaaaaaaaaaaaaaaaaaa"); //// 设置默认语言slr.setDefaultLocale(Locale.CHINESE); return slr; } @Beanpublic LocaleChangeInterceptor localeChangeInterceptor() {LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); // 设置参数名lci.setParamName("lang"); return lci; } public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(localeChangeInterceptor()); }}
LocaleMessageSourceService
/*** *@purpose:本地化语言信息工具类 *@since:2018年3月30日 ***/@Servicepublic class LocaleMessageSourceService { @Autowired private MessageSource messageSource; /****@param code:对应messages配置的key.*@return*/ public String getMessage(String code){ //这里使用比较方便的方法,不依赖request.Locale locale = LocaleContextHolder.getLocale(); String lang = locale.getLanguage(); if(LanguageConstant.LANGUAGE_FR.equals(lang)) {locale = new Locale(lang,"FR"); }if(LanguageConstant.LANGUAGE_ZH.equals(lang)) {locale = new Locale(lang,"CN"); }if(LanguageConstant.LANGUAGE_EN.equals(lang)) {locale = new Locale(lang,"US"); }String message = messageSource.getMessage(code,null,locale); return message; }/*** 获得当前语言* @param code* @return*/public String getCurrentLanguage(){ Locale locale = LocaleContextHolder.getLocale(); String lang = locale.getLanguage(); if(StringUtils.isEmpty(lang)) {lang = LanguageConstant.LANGUAGE_EN; }return lang; }}
3、成果展现
文章图片
在网关服务之中切换语言(en)后获得信息
文章图片
ai服务的语言体系
文章图片
**dx服务的语言体系
文章图片
网关切换为中文(zh)后其他服务语言返回
文章图片
ai服务语言切换结果
文章图片
**dx服务语言切换结果
文章图片
4、总结 如果使用简单粗暴的方法,直接通过网关调用其他微服务;虽然能够实现功能;但是此方法比较笨重;同时书写的代码比较多。为后期维护带来很大的不便利。
如果使用方案二在网关层进行统一处理,借助语言切换的能够在网关(Zuul)服务进行暂存和传递;同时动态添加切换语言后的参数到Request之中;能够便捷的把语言参数带到其他微服务之中。此方法值得推崇。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- SpringCloudAlibaba|SpringCloudAlibaba 微服务讲解(四)Sentinel--服务容错(二)
- 业务网关之AK中心建设
- 网关|突发!Spring Cloud 爆高危漏洞。。赶紧修复!!
- Java|云原生DevOps(k8s/docker/springcloudalibaba)
- 漏洞通报|CVE-2022-22947 SpringCloud Gateway 远程命令执行漏洞
- SpringCloud中使用Sentinel实现限流的实战
- Serverless Kubernetes 再升级 | 全新的网关能力增强
- springcloud|SpringCloud之@SpringBootApplication
- java|springcloud-alibaba springSecurity整合oauth2 授权码模式
- eBay|eBay 基于 Apache Kyuubi 构建统一 Serverless Spark 网关的实践