项目背景:目前使用的springcloud微服务架构,开发人员本地联调过程中,会用到许多并非自己开发的微服务支持。但是这样就需要启动多个应用,严重影响开发效率。现在架构组讨论写一个feign重负载,可以指定一次请求负载到具体ip。
大致想法:重写feign的负载均衡客户端LoadBalancerFeignClient,每次请求会执行excute方法,在excute方法中获取指定ip,替换feign已经负载好的ip。
遇到问题:配置类没加getset方法,导致无法读取配置文件;DiscoveryClient获取服务列表用getApplication方法,之前用的getInstance获取不到;版本问题,10.7.4feign-core版本拿到的url是ip,需要将ip替换为applicationname,否则调用多个feignclient会报错,具体原因还没扒出来。
上代码:
重写的负载客户端:
@Slf4j
public class FeignReBalancer extends LoadBalancerFeignClient {
private Client delegate;
private CachingSpringLoadBalancerFactory lbClientFactory;
private SpringClientFactory clientFactory;
private ReBalancerProperties reBalancerProperties;
public FeignReBalancer(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory, ReBalancerProperties reBalancerProperties, DiscoveryClient discoveryClient){
super(delegate, lbClientFactory, clientFactory);
this.delegate = delegate;
this.lbClientFactory = lbClientFactory;
this.clientFactory = clientFactory;
this.reBalancerProperties = reBalancerProperties;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
log.info("feign开始负载均衡...");
//重新负载后的url
String balanceUrl = null;
//原始url
String url = request.url();
//应用名
String applicationName = request.requestTemplate().feignTarget().name();
balanceUrl = newURL(url,applicationName);
URI uri = URI.create(url);
//应用ip
String clientName = uri.getHost();
int port = uri.getPort();
//发起请求客户端的IP
String requestIp = PubFun.threadLocal.get()==null?null:PubFun.threadLocal.get().getClientIp();
//获取注册中心的所有服务ip
DiscoveryClient discoveryClient = SpringContextUtils.getBeanByClass(DiscoveryClient.class);
Application application = discoveryClient.getApplication(applicationName.toUpperCase());
List<InstanceInfo> instances = application.getInstances();
//如果ip没有注册,则走默认
if(!instances.stream().anyMatch(instance -> instance.getHostName().equals(requestIp))){
if(reBalancerProperties.servers != null &&
reBalancerProperties.servers.keySet().stream().anyMatch(key -> key.equals(applicationName))){
clientName = reBalancerProperties.servers.get(applicationName);
}else{
}
}else{
//ip在注册中心,则替换为传入的ip
clientName = requestIp;
}
//设置负载服务
List<Server> allServers = this.clientFactory.getLoadBalancer(clientName).getAllServers();
String finalHost = clientName;
//如果不存在ip的负载服务,则设置,存在不需要设置
if(!allServers.stream().anyMatch(server -> finalHost.equals(server.getHost()))){
this.clientFactory.getLoadBalancer(clientName).addServers(Arrays.asList(new Server(clientName, port),new Server(applicationName)));
}
Response response = this.getResponse(request, options, balanceUrl);
log.info("feign自定义负载至:"+clientName+":"+port+"完毕!");
return response;
}
private Response getResponse(Request request, Request.Options options, String newUrl) throws IOException {
//重新构建 request 对象
Request newRequest = Request.create(request.method(),
newUrl, request.headers(), request.body(),
request.charset());
return super.execute(newRequest, options);
}
/**
* 将ip替换
* @param url
* @param ipAddress
* @return
*/
private String newURL(String url,String ipAddress){
String[] split = url.split("/");
split[2] = ipAddress;
return Arrays.stream(split).reduce((s1, s2) -> s1+"/"+s2).get();
}
}
注入上面的负载客户端
@ConditionalOnProperty(prefix = ReBalancerProperties.prefix,name = "enable",havingValue = "true")
@Configuration
@EnableConfigurationProperties(value = {ReBalancerProperties.class})
public class ReBalancerConfiguration {
@Bean
public Client feignReBalancer(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, @Autowired(required = false) DiscoveryClient discoveryClient, ReBalancerProperties reBalancerProperties) {
return new FeignReBalancer(new Client.Default(null, null),
cachingFactory, clientFactory, reBalancerProperties, discoveryClient);
}
}
配置类:
@ConfigurationProperties(prefix = ReBalancerProperties.prefix)
@Data
public class ReBalancerProperties {
static final String prefix = "rebalancer";
public Map<String,String> servers;
public String test;
}
补充:自己的负载client是在TraceFeignAspect切面类加载的
@Aspect
class TraceFeignAspect {
private static final Log log = LogFactory.getLog(TraceFeignAspect.class);
private final BeanFactory beanFactory;
TraceFeignAspect(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Around("execution (* feign.Client.*(..)) && !within(is(FinalType))")
public Object feignClientWasCalled(final ProceedingJoinPoint pjp) throws Throwable {
Object bean = pjp.getTarget();
Object wrappedBean = (new TraceFeignObjectWrapper(this.beanFactory)).wrap(bean);
if (log.isDebugEnabled()) {
log.debug("Executing feign client via TraceFeignAspect");
}
return bean != wrappedBean ? this.executeTraceFeignClient(bean, pjp) : pjp.proceed();
}
Object executeTraceFeignClient(Object bean, ProceedingJoinPoint pjp) throws IOException {
Object[] args = pjp.getArgs();
Request request = (Request)args[0];
Request.Options options = (Request.Options)args[1];
return ((Client)bean).execute(request, options);
}
} 我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano