搭建服务测试环境

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

主要步骤如下:

1.引入Eureka所需依赖

org.springframework.cloud
spring-cloud-starter-eureka-server

org.springframework.cloud
spring-cloud-starter-eureka

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
publicclassEurekaServerPort9001_App{
publicstaticvoidmain(String[]args){
SpringApplication.run(EurekaServerPort9001_App.class,args);
}
}

客户端:

@SpringBootApplication
@RefreshScope
@EnableEurekaClient
publicclassLicenseApplication_8002{
publicstaticvoidmain(String[]args){
SpringApplication.run(LicenseApplication_8002.class,args);
}
}

4.测试

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

如图:

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

消费者搭建

1.Discover Client方式

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

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
publicclassConsumerApplication_7002{
publicstaticvoidmain(String[]args){
SpringApplication.run(ConsumerApplication_7002.class,args);
}
}

核心配置类:

@Component
publicclassConsumerDiscoveryClient{

@Autowired
privateDiscoveryClientdiscoveryClient;

publicServiceInstancegetServiceInstance(){
ListserviceInstances=discoveryClient.getInstances(“licensingservice”);
if(serviceInstances.size()==0){
returnnull;
}
returnserviceInstances.get(0);
}

publicStringgetUrl(Stringurl){
ServiceInstanceserviceInstance=this.getServiceInstance();
if(serviceInstance==null)
thrownewRuntimeException(“404,NOTFOUND”);
StringurlR=String.format(url,serviceInstance.getUri().toString());
returnurlR;
}
}

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

测试Controller

@RestController
@RequestMapping(“test”)
publicclassTestController{
//注意必须new,否则会被ribbon拦截器拦截,改变URL行为
privateRestTemplaterestTemplate=newRestTemplate();
@Autowired
privateConsumerDiscoveryClientconsumerDiscoveryClient;
@RequestMapping(“/getAllEmp”)
publicListgetAllLicense(){
Stringurl=consumerDiscoveryClient.getUrl(“%s/test/getAllEmp”);
returnrestTemplate.getForObject(url,List.class);
}
}

测试:

  1. 调试信息

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

  1. 页面返回信息

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

2.Ribbon功能的Spring RestTemplate方式

依赖同上。

核心配置类:

//指明负载均衡算法
@Bean
publicIRuleiRule(){
returnnewRoundRobinRule();
}

@Bean
@LoadBalanced//告诉Spring创建一个支持Ribbon负载均衡的RestTemplate
publicRestTemplaterestTemplate(){
returnnewRestTemplate();
}

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

测试类:

@RestController
@RequestMapping(“rest”)
publicclassConsumerRestController{

@Autowired
privateRestTemplaterestTemplate;
privatefinalstaticStringSERVICE_URL_PREFIX=”http://LICENSINGSERVICE”;

@RequestMapping(“/getById”)
publicEmpgetById(Longid){
MultiValueMapparamMap=newLinkedMultiValueMap();
paramMap.add(“id”,id);
returnrestTemplate.postForObject(SERVICE_URL_PREFIX+”/test/getById”,paramMap,Emp.class);
}
}

测试结果:

  • 第一次:

  • 第二次:

  • 第三次:

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

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

3.feign客户端方式

引入依赖:

org.springframework.cloud
spring-cloud-starter-feign

主要代码:

@FeignClient(value=”LICENSINGSERVICE”,fallbackFactory=ServiceImp.class)
publicinterfaceServiceInterface{

@RequestMapping(“/test/getById”)
EmpgetById(@RequestParam(“id”)Longid);

@RequestMapping(“/test/getLicenseById”)
LicensegetLicenseById(@RequestParam(“id”)Longid);

@RequestMapping(“/test/getAllEmp”)
ListgetAllLicense();
}
@Component
publicclassServiceImpimplementsFallbackFactory{

@Override
publicServiceInterfacecreate(Throwablethrowable){
returnnewServiceInterface(){
@Override
publicEmpgetById(Longid){
Empemp=newEmp();
emp.setName(“iamfeignfallbackcreate”);
returnemp;
}

@Override
publicLicensegetLicenseById(Longid){
returnnull;
}

@Override
publicListgetAllLicense(){
returnnull;
}
};
}
}

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

主启动类:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
publicclassConsumer_feign_Application_7004{
publicstaticvoidmain(String[]args){
SpringApplication.run(Consumer_feign_Application_7004.class,args);
}
}

测试类:

@RestController
@RequestMapping(“rest”)
publicclassConsumerRestController{
@Autowired
privateServiceInterfaceserviceInterface;

@Autowired
privateRestTemplaterestTemplate;
@RequestMapping(“/getById”)
publicEmpgetById(Longid){
returnserviceInterface.getById(id);
}
}

测试结果:

  • 正常测试:

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

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

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