在上一篇笔记-注册中心中,服务sample只是简单实现了eureka客户端,现在为它添加一个controller
1 2 3 4 5 6 7 8 9 10 11
| @Controller @RequestMapping("/") public class TestController { @ResponseBody @RequestMapping("param") public Object test(HttpServletRequest request){ Map params = HttpServletUtil.getRequestParameters(request); params.put("msg","请求成功"); return params; } }
|
这个接口很简单,获取请求参数,在参数Map中加一个msg,然后再原样返回给请求方。
通常来说,请求方可以直接通过ip:port/param?key=value
的形式访问sample,但是这样做就跟传统的web项目没区别了,注册中心也就失去了它本来的意义。
在spring cloud中,服务的请求有两种方式,一种是ribbon+restTemplate
,另一种是feign
,下面分别介绍下两种方式的使用方式。
比如存在另一个服务service1,需要在sample服务中调用service1服务,两种实现方式如下
继续在sample工程中添加依赖
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
|
ribbon+restTemplate
使用ribbon和断路器时需要在主类上添加@EnableHystrix
注解。
创建restTemplate的bean
1 2 3 4 5
| @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }
|
创建一个bean,封装一些访问的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Bean public class RibbonClient{ @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "callError") public String postForObject(String serviceId, String methodMapping, Object params) { return restTemplate.postForObject("http://"+serviceId+"/"+methodMapping,params,String.class); } @HystrixCommand(fallbackMethod = "callError") public String postForObject(String serviceId, String controllerMapping, String methodMapping, Object params) { return restTemplate.postForObject("http://"+serviceId+"/"+controllerMapping+"/"+methodMapping,params,String.class); } @HystrixCommand(fallbackMethod = "callError") public String getForObject(String serviceId, String methodMapping, String params) { return restTemplate.getForObject("http://"+serviceId+"/"+methodMapping+"?"+params,String.class); } @HystrixCommand(fallbackMethod = "callError") public String getForObject(String serviceId, String controllerMapping, String methodMapping, String params) { return restTemplate.getForObject("http://"+serviceId+"/"+controllerMapping+"/"+methodMapping+"?"+params,String.class); } public String callError(String serviceId, String methodMapping, Object params){ return "调用服务"+serviceId+"发生错误,服务可能已下线,请稍后再试"; } public String callError(String serviceId, String controllerMapping, String methodMapping, Object params){ return "调用服务"+serviceId+"发生错误,服务可能已下线,请稍后再试"; } public String callError(String serviceId, String methodMapping, String params){ return "调用服务"+serviceId+"发生错误,服务可能已下线,请稍后再试"; } public String callError(String serviceId, String controllerMapping, String methodMapping, String params){ return "调用服务"+serviceId+"发生错误,服务可能已下线,请稍后再试"; } }
|
上面代码中分别封装了两个POST方法和两个GET方法,使用restTemplate的getForObject或postForObject方法发送get或post请求,这里直接使用serviceId代替具体的url地址,在ribbon中它会根据服务名来选择具体的服务实例,在请求时使用具体的url替换掉服务名。
方法上面的注解@HystrixCommand(fallbackMethod = "callError")
就是断路器,在请求的服务不可用时可以快速返回,避免因为单个服务出现问题,调用者出现线程阻塞。在上面代码中,如果被调用服务不可用,则调用者会回退调用指定的CallError方法。
restTemplate.postForObject
方法的第二个参数是服务的请求入参,方法上指定的类型是Object,实际上最好是HttpEntity
对象,一个构建HttpEntity
方法示例如下,实际可根据业务需求修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| protected HttpEntity buildPostParam(Object params,HttpServletRequest request){ HttpHeaders headers = new HttpHeaders(); String contentType = request.getContentType(); if(StringUtils.isBlank(contentType)){ contentType = "text/plain;charset=UTF-8"; } headers.setContentType(MediaType.parseMediaType(contentType)); HttpEntity<String> formEntity = null; if(params instanceof String){ formEntity = new HttpEntity<String>((String)params, headers); }else{ formEntity = new HttpEntity<String>(JSONUtil.toJSONString(params), headers); } return formEntity; }
|
feign
feign是一个声明式的伪Http客户端,采用基于接口的注解,默认集成了ribbon并和eureka结合,实现了客户端负载均衡。
需要在主类添加@EnableFeignClients
注解。
创建一个接口
1 2 3 4 5
| @FeignClient(value = "service1",,fallback = TestClientHiHystric.class) public interface TestClient { @RequestMapping(value = "test/param",method = RequestMethod.POST) public String callTest(@RequestParam(value = "name") String value); }
|
实现断路器,需要修改application.yml配置文件,实现TestClient接口并注入到Ioc容器中
1 2 3
| feign: hystrix: enabled: true
|
1 2 3 4 5 6 7
| @Component public class TestClientHiHystric implements TestClient { @Override public String callTest(String value) { return "sorry "+name; } }
|
通过@FeignClient
注解的value值指定了该接口调用的是service1服务,fallback值指定了断路器,方法上的注解指定了调用服务service1上的test/param地址,使用POst方式。方法入参指定了参数名和值。方法的返回类型指定了调用该服务接口的返回类型。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Controller @RequestMapping("test") public class ClientTestController { @Autowired TestClient testClient; @ResponseBody @RequestMapping("test") public Object hi(){ String response = testClient.callTest("zhagnsan"); return response; } }
|
上面例子中在sample服务的一个controller中调用另外一个服务service1的test/param接口,参数是name=zhagnsan。
对于服务sample来说,并不知道服务service1的具体地址,也不知道到底有几个service1的实例在运行,仅仅需要指定需要调用的服务名就行,feign自己会从eureka注册中心获取服务service1的url信息完成调用。