转载请标明出处:
http://blog.csdn.net/forezp/article/details/69808079
本文出自方志朋的博客
本文都是按照方志朋博客学习所做笔记,作者做的很详细,也很清晰。但可能是spring cloud有东西太多的原因,包括jar包版本之类的,跟着做的时候出现了许多的问题。
在作者的博客下面也有许多的评论,提问。有许多问题我也碰到了,但运气好,我的都解决了。
在本篇结尾有我遇到的坑和填坑方法。
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。在这一篇文章首先讲解下基于ribbon+rest。
在实际开发中,一个业务的完成,常常会需要服务A调用服务B,这个过程是服务B的消费。
服务消费者:rest+ribbon
ribbon简介
ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
使用ribbon消费服务的时候,如果服务有多个实例,ribbon会根据默认根据轮流的策略消费这些实例。
准备
接着上个项目,将eureka创建两个实例。方法:见原博客—–开启多个服务实例
创建消费者
1,同最开始创建Eurake的服务端一样,创建一个springBoot项目:ribbondemo。
只要选择,spring-cloud-starter-netflix-eureka-server这个依赖,因为其中包涵了ribbon的依赖
pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2, 创建配置文件如下:
eureka:
client:
service-url:
defaultZone: http://localhost:8780/eureka/
server:
port: 8789
spring:
application:
name: service-ribbon
3,在主程序上通过@EnableDiscoveryClient向服务中心注册。
并通过@Bean向容器中注入RestTemplate,再使用@LoadBalanced开启负载均衡
//向服务中心注册
@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
/**
* 向spring容器中注入一个bean,并通过LoadBalanced来开启负载均衡
* @return
*/
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
4, 在创建一个Service类,在类通过RestTemplate对象调用服务对象消费服务。
@Service
public class ServiceTest {
@Autowired
RestTemplate restTemplate;
//SERVICE-HI替代了具体的url, 因为ribbon会根据具体的服务名替代服务实例。
public String hiService(String hi){
return restTemplate.getForObject("http://SERVICE-HI/hi?name="+hi,String.class);
}
}
5,创建Controller,接收浏览器请求。
并通过service来调用服务。
@RestController
public class TestController {
@Autowired
ServiceTest serviceTest;
@RequestMapping("/hihihi")
public String hi(@RequestParam String name){
System.out.println("----------" + name);
return serviceTest.hiService(name);
}
}
6:结果
eureka就有两个服务,第一个服务拥有两个实例。可以通过不同的端口访问。
通过ribbon,输入http://localhost:8789/hihihi?name=ribbon来消费服务
多次访问结果在下面两个中切换:
hi ribbon,i am from port:8781,time:Mon Sep 03 17:36:58 CST 2018
hi ribbon,i am from port:8782,time:Mon Sep 03 17:38:02 CST 2018
所以,负载均衡在我们调用下面这个方法时做到的。
restTemplate.getForObject(“http://SERVICE-HI/hi?name=“+hi,String.class);
服务消费者:feign
简介
- 基于接口
- 整合了ribbon
创建feign消费者
1,同样的需要前面的eureka客户端的两个实例。
2,同样的创建springboot的项目:feign
但是需要重新导入feign的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3,但修改application.yml配置文件:
eureka:
client:
service-url:
defaultZone: http://localhost:8780/eureka/
server:
port: 8788
spring:
application:
name: service-feign
4,在应用程序上是用注解开启Feign的功能
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class FeigndemoApplication {
public static void main(String[] args) {
SpringApplication.run(FeigndemoApplication.class, args);
}
}
5,创建接口,使用注解@FeignClient指定调用注册中心的服务
@FeignClient(value = "service-hi")
public interface SchedualServiceHi {
@RequestMapping(value = "/hi", method = RequestMethod.GET)
String sayhi(@RequestParam(value = "name") String name);
}
6,再创建Contrloller,对外暴露”/hihihi”,注入接口来消费服务。
@RestController
public class SayHiController {
@Resource
private SchedualServiceHi schedualServiceHi;
@RequestMapping(value = "/hi", method = RequestMethod.GET)
String sayHi(@RequestParam String name){
return schedualServiceHi.sayhi(name);
}
}
7,结果
通过http://localhost:8788/hihihi?name=feign访问:
hi feign,i am from port:8782,time:Mon Sep 03 18:09:29 CST 2018
hi feign,i am from port:8781,time:Mon Sep 03 18:09:53 CST 2018
问题
ps1: 按照作者的方法导入依赖,许多依赖都是unknow? 所以注解都找不到相应类。
与原作者不同,我使用springboot自带的eureka server的spring cloud 版本是Finchley.SR1
所以后面导入ribbon,feign的骨架id都不同。
- 客户端也只需同服务端一样,从springBoot中选中即可
依赖主要是:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- ribbon,不需要导其他依赖。同上。
- feign,他的id是 …..-openfeign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
ps2: 我也出现过404的情况了,出现了下面这种报错
可是requestmapping明明都没有配错,一起都好好的啊。
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Sep 03 18:25:46 CST 2018
There was an unexpected error (type=Not Found, status=404).
No message available
那原因即有可能是你controller的注解使用的是@Controller.
这里必须得用@RestController.
ps3:我还出现了400的报错。
原因是请求的参数名与controller中的handler里接受的参数名不同导致的。
eg: 假如你请求是这样:http://localhost:/hi?name=cloud
而你的handler是这样。
@RequestMapping(value = “/hi”, method = RequestMethod.GET)
String sayHi(@RequestParam String hi){
return schedualServiceHi.sayhi(hi);
}
那对不起,他会报400。必要的参数hi,不存在。