1. 场景
在学习Ribbon + RestTemplate 测试Ribbon的负载均衡功能时,发现RestTemplate 使用服务提供者的服务名去请求Rest接口时,报null 指针异常。
2. 代码结构如下
2.1 服务提供者
(1) application.yml
server:
port: 7900
spring:
application:
name: eureka_producer_user
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql
data: classpath:data.sql
logging:
level:
root: info
org.hibernate: info
org.hibernate.type.descriptor.sql.BasicBinder: trace
org.hibernate.type.descriptor.sql.BasicExtractor: trace
com.example.spring.cloud: debug
eureka:
client:
heathcheck:
enabled: true
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
(2) Application类
@SpringBootApplication
@EnableDiscoveryClient
public class ProducerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerUserApplication.class, args);
}
}
(3) 对外暴露的Controller类 的接口
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/simple/{id}")
public User findById(@PathVariable(name = "id") Long id) {
return userRepository.findById(id);
}
}
2.2 服务消费者
(1) application.yml
server:
port: 8010
spring:
application:
name: eureka_consumer_movie_ribbon
logging:
level:
root: info
com.example.spring.cloud: debug
eureka:
client:
heathcheck:
enabled: true
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
(2) Application类
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
}
(3) Controller类
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id) {
// VIP virtual IP
// HAProxy Heartbeat
return restTemplate.getForObject("http://eureka_producer_user/simple/" + id, User.class);
}
}
3. 异常
请求 http://localhost:8010/user/1 时抛出如下异常:
异常
4. 异常排查
通过对异常栈进行debug ,发现是由于URI 类对于http://host:port/path这种结构 中的host 属性,不支持host中含_(下划线),不然解析出来的host就为null, 最终导致抛出NullPointerException。
5. 相关issues
https://github.com/spring-cloud/spring-cloud-netflix/issues/1582
https://github.com/spring-cloud/spring-cloud-netflix/issues/263