搭建服务测试环境

测试中,服务发现层采用Netflix的Eureka搭建。

主要步骤如下:

1.引入Eureka所需依赖


<!--eureka服务端--> 

   
<dependency> 

     
<groupId>org.springframework.cloud 
</groupId> 

     
<artifactId>spring-cloud-starter-eureka-server 
</artifactId> 

   
</dependency> 

<!--客户端--> 

 
<dependency> 

   
<groupId>org.springframework.cloud 
</groupId> 

   
<artifactId>spring-cloud-starter-eureka 
</artifactId> 

 
</dependency> 

2.修改配置文件
服务端:
 
eureka: 

  
instance: 

   
hostname:  
eureka9001.com  
#eureka服务端的实例名称 

   
instance-id:  
eureka9001 

 
client: 

   
register-with-eureka:  
false  
#false表示不向注册中心注册自己 

   
fetch-registry:  
false  
# #false 表示自己就是注册中心,职责就是维护服务实例,并不需要去检索服务 

   
service-url: 

   
defaulteZone:  
http://127.0.0.1:9001 

客户端1:
 
server: 

   
port:  
8002 

 
spring: 

   
application: 

     
name:  
licensingservice 

 
eureka: 

   
instance: 

     
instance-id:  
licensing-service-8002 

     
prefer-ip-address:  
true 

   
client: 

     
register-with-eureka:  
true 

     
fetch-registry:  
true 

     
service-url: 

       
defaultZone:  
http://127.0.0.1:9001/eureka/, 

客户端2:
 
server: 

  
port:  
8002 

 
spring: 

  
application: 

    
name:  
licensingservice 

 
eureka: 

  
instance: 

    
instance-id:  
licensing-service-8002 

    
prefer-ip-address:  
true 

  
client: 

    
register-with-eureka:  
true 

    
fetch-registry:  
true 

    
service-url: 

      
defaultZone:  
http://127.0.0.1:9001/eureka/,    



一组微服务的不同实例采服务名相同,不同的实例Id区分,分别对应,spring.application.name 和eureka.instance.instance-id。

3.启动服务

服务端:

@SpringBootApplication 

 
@EnableEurekaServer 

 
public  
class EurekaServerPort9001_App { 

   
public static void main(String[] args) { 

    SpringApplication.run(EurekaServerPort9001_App 
.class,args); 

  } 

} 

客户端:
 
@SpringBootApplication 

 
@RefreshScope 

 
@EnableEurekaClient 

 
public  
class LicenseApplication_8002 { 

     
public static void main(String[] args) { 

        SpringApplication.run(LicenseApplication_8002 
.class, args); 

    } 

}


4.测试

进入到Eureka服务端地址,我这是127.0.0.1:9001,可以查看注册到注册中心的服务。

如图:


springcloud 通过服务名动态调用 springcloud怎么调用服务_java-ee

注意事项:Eureka通过三次心跳检测均通过,服务才会成功注册到注册中心,默认每次间隔10s,及初次启动服务需等待30s才能在Eureka中看到注册服务。

消费者搭建

1.Discover Client方式

微服务中服务既是消费者也可以是调用者,因此消费者配置和上面服务配置大体一致,依赖及配置参考上面服务端搭建方式。启动主类添加EnableEurekaClient注释:

@SpringBootApplication 

 
@EnableEurekaClient 

 
@EnableDiscoveryClient 

 
public  
class ConsumerApplication_7002 { 

     
public static void main(String[] args) { 

        SpringApplication.run(ConsumerApplication_7002 
.class, args); 

    } 

} 

核心配置类:
 
@Component 

 
public  
class ConsumerDiscoveryClient { 


   
@Autowired 

   
private DiscoveryClient discoveryClient; 


   
public ServiceInstance getServiceInstance() { 

    List<ServiceInstance> serviceInstances = discoveryClient.getInstances( 
"licensingservice"); 

     
if (serviceInstances.size() ==  
0) { 

       
return  
null; 

    } 

     
return serviceInstances.get( 
0); 

  } 


   
public String getUrl(String url) { 

    ServiceInstance serviceInstance= 
this.getServiceInstance(); 

     
if (serviceInstance== 
null) 

       
throw  
new RuntimeException( 
"404 ,NOT FOUND"); 

    String urlR=String.format(url,serviceInstance.getUri().toString()); 

     
return urlR; 

  } 

}


通过DiscoveryClient从Eureka中获取licensingservice服务的实例数组,并返回第一个实例。

测试Controller

@RestController 

 
@RequestMapping( 
"test") 

 
public  
class TestController { 

   
//注意必须new,否则会被ribbon拦截器拦截,改变URL行为 

   
private RestTemplate restTemplate= 
new RestTemplate(); 

   
@Autowired 

   
private ConsumerDiscoveryClient consumerDiscoveryClient; 

   
@RequestMapping( 
"/getAllEmp") 

   
public List<Emp> getAllLicense(){ 

    String url=consumerDiscoveryClient.getUrl( 
"%s/test/getAllEmp"); 

     
return restTemplate.getForObject(url,List 
.class); 

  } 

}


测试:

  1. 调试信息

springcloud 通过服务名动态调用 springcloud怎么调用服务_java_02

从该图可以直观看到licensingservice,拥有两个服务实例,并可以查看实例信息。

  1. 页面返回信息

springcloud 通过服务名动态调用 springcloud怎么调用服务_架构_03

成功查询到数据库存储信息。

2.Ribbon功能的Spring RestTemplate方式

依赖同上。

核心配置类:

//指明负载均衡算法 

 @Bean 

 public IRule iRule() { 

   return  new RoundRobinRule(); 

} 


 @Bean 

 @LoadBalanced  //告诉Spring创建一个支持Ribbon负载均衡的RestTemplate 

 public RestTemplate restTemplate() { 

   return  new RestTemplate(); 

}


设置负载均衡方式为轮询方式。

测试类:

@RestController 

 @RequestMapping( "rest") 

 public  class ConsumerRestController { 


     @Autowired 

     private RestTemplate restTemplate; 

     private  final  
static String SERVICE_URL_PREFIX =  
"http://LICENSINGSERVICE"; 


     @RequestMapping( "/getById") 

     public Emp getById(Long id) { 

        MultiValueMap<String, Object> paramMap =  new LinkedMultiValueMap<String, Object>(); 

        paramMap.add( "id", id); 

         return restTemplate.postForObject(SERVICE_URL_PREFIX +  "/test/getById", paramMap, Emp 
.class); 

    } 

}


测试结果:

  • 第一次:

springcloud 通过服务名动态调用 springcloud怎么调用服务_架构_04

  • 第二次:

springcloud 通过服务名动态调用 springcloud怎么调用服务_java-ee_05

  • 第三次:

springcloud 通过服务名动态调用 springcloud怎么调用服务_架构_06

因为采用轮询负载均衡方式分别调用不同服务实例,未区别,将name做出了一定更改。

以上两种方式对比,Ribbon方式是对第一种方式的封装且内置不同的负载算法,支持自定义。使用更加简单,但此两次均需编写RestTemplate的请求方法,较为繁琐且容易出错,第三种方式Feign客户端则极大的降低了开发难度和提升速度。

3.feign客户端方式

引入依赖:

<dependency> 

     <groupId>org.springframework.cloud </groupId> 

     <artifactId>spring-cloud-starter-feign </artifactId> 

 </dependency>


主要代码:

@FeignClient(value =  "LICENSINGSERVICE",fallbackFactory = ServiceImp .class)
public interface ServiceInterface { 


   @RequestMapping( "/test/getById") 

   Emp getById(@RequestParam("id") Long id); 


   @RequestMapping( "/test/getLicenseById") 

   License getLicenseById(@RequestParam("id") Long id); 


   @RequestMapping( "/test/getAllEmp") 

   List<Emp> getAllLicense(); 

} 

 @Component 

 public  class ServiceImp implements FallbackFactory<ServiceInterface> { 


     @Override 

     public ServiceInterface create(Throwable throwable) { 

         return  new ServiceInterface() { 

             @Override 

             public Emp getById(Long id) { 

                Emp emp =  new Emp(); 

                emp.setName( "i am feign fallback create"); 

                 return emp; 

            } 


             @Override 

             public License getLicenseById(Long id) { 

                 return  null; 

            } 


             @Override 

             public List<Emp> getAllLicense() { 

                 return  null; 

            } 

        }; 

    } 

}


采用接口模式开发,通过注解指明服务名以及后备方法,在服务表现不佳时,方便返回默认的结果,而不是一个不友好的错误。

主启动类:
 @SpringBootApplication 

 @EnableEurekaClient 

 @EnableFeignClients 

 public  class Consumer_feign_Application_7004 { 

     public static void main(String[] args) { 

        SpringApplication.run(Consumer_feign_Application_7004 .class, args); 

    } 

} 

测试类:
 @RestController 

 @RequestMapping( "rest") 

 public  class ConsumerRestController { 

   @Autowired 

   private ServiceInterface serviceInterface; 


   @Autowired 

   private RestTemplate restTemplate; 

   @RequestMapping( "/getById") 

   public Emp getById(Long id) { 

     return serviceInterface.getById(id); 

  } 

}

测试结果:

  • 正常测试:

springcloud 通过服务名动态调用 springcloud怎么调用服务_java-ee_07

  • 关闭两个服务实例,模拟服务实例死亡:

springcloud 通过服务名动态调用 springcloud怎么调用服务_后端_08

Feign除了能简化服务调用,也可以实现当调用的服务失败时,友好的反馈信息。

此三种调用方式,由低至上,从不同层次实现了SpringCloud中的微服务互相调用。