研发提速:nacos+openfeign环境下的本地链接服务
项目研发过程中,经常会遇到与测试人员工作重叠的情况,十分影响效率。
做了一个修改,可以在本地环境启动项目后和测试环境交互,并且不影响测试环境,理论上也可以用于线上环境的异常的快速处理。
准备事项如下:
一:搭建本地的nacos服务。
二:导入测试环境相应项目的nacos配置文件。
三:新增代码:
修改LoadBalancerFactory获取服务host的方式,由于是本地启动的项目,并且连接的还是本地的nacos,所以项目启动后,肯定不会注册到测试环境,相对的也获取不到测试环境的其他服务。
由于本人使用的时候是基于CachingSpringLoadBalancerFactory ,如果直接使用时不生效或者异常,可以DEBUG跟踪一下自己框架调用服务时使用的具体LoadBalancerFactory类。
3 import cn.hutool.http.HttpUtil; 4 import com.alibaba.fastjson.JSON; 5 import com.alibaba.fastjson.JSONArray; 6 import com.alibaba.fastjson.JSONObject; 7 import com.netflix.client.config.IClientConfig; 8 import com.netflix.loadbalancer.ILoadBalancer; 9 import com.netflix.loadbalancer.Server; 10 import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory; 11 import org.springframework.cloud.netflix.ribbon.ServerIntrospector; 12 import org.springframework.cloud.netflix.ribbon.SpringClientFactory; 13 import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; 14 import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; 15 import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer; 16 import org.springframework.util.ConcurrentReferenceHashMap; 17 import org.springframework.util.ObjectUtils; 18 19 import java.util.ArrayList; 20 import java.util.List; 21 import java.util.Map; 22 23 public class DevCachingSpringLoadBalancerFactory extends CachingSpringLoadBalancerFactory { 24 25 private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>(); 26 private volatile Map<String, List<Server>> server = new ConcurrentReferenceHashMap<>(); 27 private volatile String ip; 28 private volatile String port; 29 private volatile String namespaceid; 30 31 public DevCachingSpringLoadBalancerFactory(SpringClientFactory factory) { 32 super(factory); 33 } 34 35 public DevCachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) { 36 super(factory, loadBalancedRetryPolicyFactory); 37 } 38 39 public boolean initialize(String ip,String port,String namespaceid) { 40 this.ip = ip; 41 this.port = port; 42 this.namespaceid = namespaceid; 43 return null != ip ? null != port ? null != namespaceid ? true : false :false :false; 44 } 45 46 @Override 47 public FeignLoadBalancer create(String clientName) { 48 FeignLoadBalancer client = this.cache.get(clientName); 49 if (client != null) { 50 return client; 51 } 52 IClientConfig config = this.factory.getClientConfig(clientName); 53 ILoadBalancer lb = this.factory.getLoadBalancer(clientName); 54 55 //修改部分 56 List<Server> list = lb.getAllServers(); 57 if (null == list || ObjectUtils.isEmpty(list)) list = new ArrayList<>(); 58 list.addAll(Servers(clientName)); 59 lb.addServers(list); 60 61 ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, 62 ServerIntrospector.class); 63 client = this.loadBalancedRetryFactory != null 64 ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, 65 this.loadBalancedRetryFactory) 66 : new FeignLoadBalancer(lb, config, serverIntrospector); 67 this.cache.put(clientName, client); 68 return client; 69 } 70 71 /** 72 * 获取server 73 * 返回数组,重试机制交由原框架 74 * http://ip:port/nacos/v1/ns/instance/list?namespaceId=namespaceid&serviceName=client 75 */ 76 public List<Server> Servers(String client) { 77 if(server.containsKey(client)) return server.get(client); 78 else synchronized (server) { 79 if(server.containsKey(client)) return server.get(client); 80 else { 81 server.put(client,new ArrayList<Server>()); 82 String url = new StringBuilder("http://") 83 .append(ip) 84 .append(":") 85 .append(port) 86 .append("/nacos/v1/ns/instance/list?") 87 .append("namespaceId=") 88 .append(namespaceid) 89 .append("&serviceName=") 90 .append(client).toString(); 91 JSONObject jsonObject = JSON.parseObject(HttpUtil.get(url)); 92 JSONArray hosts = jsonObject.getJSONArray("hosts"); 93 for (int i = 0; i < hosts.size(); i++) { 94 server.get(client).add(new Server(hosts.getJSONObject(i).getString("ip"),hosts.getJSONObject(i).getInteger("port"))); 95 } 96 return server.get(client); 97 } 98 } 99 } 100 101 }
这个文件可以不用添加,主要是用来做一些其他的扩展。
3 import feign.Client; 4 import feign.Request; 5 import feign.Response; 6 import org.springframework.cloud.netflix.ribbon.SpringClientFactory; 7 import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; 8 import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; 9 10 import java.io.IOException; 11 12 public class DevFeignClient extends LoadBalancerFeignClient{ 13 14 public DevFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory, SpringClientFactory clientFactory) { 15 super(delegate, lbClientFactory, clientFactory); 16 } 17 18 @Override 19 public Response execute(Request request, Request.Options options) throws IOException { 20 return super.execute(request, options); 21 } 22 }
配置文件用来替换原来IOC中的BEAN,另外用于获取后面yml文件中的自定义配置。
3 import feign.Client; 4 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 6 import org.springframework.cloud.netflix.ribbon.SpringClientFactory; 7 import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 11 @Configuration 12 public class DevFeignConfig { 13 14 @ConditionalOnProperty("feign.dev.enabled") 15 @Configuration(proxyBeanMethods = false) 16 class DefaultFeignLoadBalancedConfiguration { 17 @Value("${feign.dev.ip}") 18 String ip; 19 @Value("${feign.dev.port}") 20 String port; 21 @Value("${feign.dev.namespaceid}") 22 String namespaceid; 23 24 25 @Bean 26 public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, DevCachingSpringLoadBalancerFactory devFactory, 27 SpringClientFactory clientFactory) { 28 System.out.println("#####################################进入本地调试模式#####################################"); 29 return new DevFeignClient(new Client.Default(null, null),null == devFactory?cachingFactory:devFactory, 30 clientFactory); 31 } 32 33 @Bean 34 public DevCachingSpringLoadBalancerFactory devFactory(SpringClientFactory factory) { 35 DevCachingSpringLoadBalancerFactory devFactory = new DevCachingSpringLoadBalancerFactory(factory); 36 if(devFactory.initialize(ip,port,namespaceid)) return devFactory; 37 System.out.println("#####################################本地调试模式异常#####################################"); 38 System.out.println("feign.dev.ip " + ip); 39 System.out.println("feign.dev.port " + port); 40 System.out.println("feign.dev.namespaceid " + namespaceid); 41 System.out.println("#####################################本地调试模式异常#####################################"); 42 return null; 43 } 44 } 45 46 47 }
修改yml文件,主要是用来配置新增的自定义属性和小插件的开启和关闭。
建议手动在本地项目的yml文件添加属性,yml文件的配置,直接复制容易出现问题。
新增属性如下:
feign.dev.enabled
feign.dev.ip
feign.dev.port
feign.dev.namespaceid
server:
port: 服务端口号
spring:
application:
name: 服务名称
profiles:
active: dev
cloud:
nacos:
config:
file-extension: yml
namespace: 本地Nacos的命名空间
username: 本地Nacos的账号
password: 本地Nacos的密码
server-addr: 本地Nacos的IP:本地Nacos的端口号
discovery:
namespace: 本地Nacos的命名空间
group: DEFAULT_GROUP
enabled: true
register-enabled: true
feign:
dev:
#true为开启本地调式,false为关闭
enabled: false
ip: 测试环境Nacos的IP
port: 测试环境Nacos的端口号
namespaceid: 测试环境Nacos的命名空间
client:
config:
default:
#不设置connectTimeout会导致readTimeout设置不生效
connectTimeout: 5000
readTimeout: 5000