spring cloud 10 feign
一、服务调用
服务调用和消息一般是两个维度,
1,服务调用是同步的
2,消息是异步的回调,事件的一个周期
二、准备
• 远程过程调用(RPC)
• 接口定义语言(IDL)
interface definition language
• 通讯协议(Protocol)
HTTP,web-socket,Socket
• Netflix Feign
契约式编程
springcloud将Feign和spring mvc整合
• Spring Cloud 技术回顾 (服务短路 、负载均衡、服务发现、分布式配置等)
image.png三、
1,NOSQL:redis,elasticsearch,mongodb
2,integration:rabbitmq,kafka,
3,config:config client/config server/zookeeper config/consul config
4,discovery:eureka server,eureka discovery,zookeeper discovery,consul discovery
5,routing:zuul,gateway,ribbon,feign
6,circuit breaker:hystrix,turbine
四、核心概念
1,远程过程调用(RPC)
remote procedure call,通过网络进行传输。
一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序(进程),而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。
• 例如
• Java RMI(二进制协议)进程之间的协议,必须用POJO(序列化&反序列化)
• WebServices(文本协议)一般都有骨架或者说是结构,比如XML schema
2,消息传递
RPC 是一种请求-响应协议,一次 RPC在客户端初始化,再由客户端将请求消息传递到远程的服务器,执行指定的带有参数(这里的参数和方法的参数有所区别。这里的参数也有可能是请求头之类的信息)的过程。经过远程服务器执行过程后,将结果作为响应内容(响应头和响应体)返回到客户端。
3,存根(Stub)
在一次分布式计算 RPC 中,客户端和服务器转化参数(不仅仅是方法的参数)的一段代码。 由于存根的参数转化,RPC执行过程如同本地执行函数调用。存根必须在客户端和服务器两端均装载,并且保持兼容(比如高版本的服务端兼容低版本的客户端调用)。
HTTP调用的存根在于内部的实现。web服务器和客户端都支持HTTP协议的话,那么协议的内容和格式就是存根。
如果是java rmi(remote method invoke)的话,两边都必须要有接口,传输的模型对象必须序列化,类的版本要保持一致,pojo的字段也要兼容。
java 序列化主要关注POJO字段的状态,对pojo的方法不是很关注。
五、spring cloud feign
• 依赖
• org.springframework.cloud:spring-cloud-starter-feign
• 激活
• @EnableFeignClients (标记在服务调用方的SpringApplication上)
• 申明
• @FeignClient 标记在接口Api上,name="服务提供方的service id"
申明@FeignClient API,name属性尽量使用占位符,避免硬编码。
package com.xixi.spring.cloud8.server.api;
import com.xixi.spring.cloud8.server.domain.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* 用户服务
* 声明Feign客户端,feigncliet里面的方法将会映射到rest api
* ${user-service-name}指向user-service-provider
*/
@FeignClient(name = "${user-service-name}")//利用占位符,避免未来整合硬编码
public interface UserService {
/**
* 保存用户
* @param user
* @return
*/
@PostMapping("/user/save")
boolean saveUser(@RequestBody User user);
/**
* 查询所有的用户列表
* @return
*/
@GetMapping("/user/list")
List<User> findAll();
}
在user-service-client客户端激活@EnableFeignClients,放在服务调用方上,申明要激活的接口。在这里是UserService.class
@EnableFeignClients(clients = {UserService.class})
public class UserServiceClientApplication{}
六、默认组件
• Decoder/Encoder : ResponseEntityDecoder / SpringEncoder
• Logger : Slf4jLogger
• Contract : SpringMvcContract
原生的feign并不支持我们spring mvc的annotation(@RequestMapping),但是和spring结合以后就支持了,靠SpringMvcContract
如果想自己扩展,可以实现Contract,Contract的作用就是Define what annotations and values are valid on interfaces.
• Feign.Builder : HystrixFeign.Builder
• Client : LoadBalancerFeignClient( Ribbon 激活的话)
feign的底层还是ribbon LoadBalancerClient那一套,
七、• 整合负载均衡: Netflix Ribbon
1,user-service-client的配置文件application.properties
###ribbon 客户端应用
spring.application.name = user-ribbon-client
eureka.client.enabled = false
##服务提供方名称
provider.service.name = user-service-provider
##服务提供方主机
provider.service.host = localhost
###服务提供方端口
provider.service.port = 8081
server.port = 8080
##关闭eureka注册,通过显示地配置服务端的地址
##定义服务提供方地址,为RibbonLoadBalancerClient提供服务列表,如果有多个可以用逗号隔开。
user-service-provider.ribbon.listOfServers = \
http://${provider.service.host}:${provider.service.port}
##配置自定义实现的MyPing,这样就不用@Bean去配置IPing,
# user-service-provider是要被负载均衡的服务
user-service-provider.ribbon.NFLoadBalancerPingClassName=\
com.xixi.spring.cloud8.server.user.ribbon.client.ping.MyPing
###配置@FeignClient(name = "${user.service.name}")中的占位符
#user.service.name需要制定user service接口的提供方,也就是user-service-provider
user.service.name = ${provider.service.name}
2,服务调用方激活@EnableFeignClients,使用user-service调用服务
在UserRibbonController中注入userServiceFeignClient
/**
* 同一个接口不建议在客户端和服务端之间共享,实际情况最好使用组合UserService的方式
* FeignClient标记的接口可以直接注入,Feign会动态代理帮我们去构造它的实现类
*/
@Autowired
private UserService userServiceFeignClient;
@GetMapping("/user/list")
public List<User> getUserList2() {
return userServiceFeignClient.findAll();
}
3,服务端实现user-service API,暴露Rest接口
首先,
@Service("inMemoryUserService")
public class InMemoryUserService implements UserService
其次,
user-service-provider是最终的服务提供方,不需要标记为feignClient
UserServiceProviderController implements UserService
UserServiceProviderController实现feignclient接口。
最后,
//由于UserServiceProviderController继承了UserService,所以这块可以不写@PostMapping("/user/save")。Feign Inheritance Support
//注意:@PostMapping会被继承,但是@RequestBody,@RequestParam是不会被继承的,method parameter mapping is not inherited
//这是因为java语言的特性决定的,方法外面的东西可以从父类继承,但是方法里面的实现,子类会覆盖父类。
//URL映射可以从父类继承。PostMapping("/user/save")
@Override
public boolean saveUser(@RequestBody User user) {
return userService.saveUser(user);
}
八、• 整合服务短路: Netflix Hystrix
1,调整UserService api
package com.xixi.spring.cloud10.server.api;
import com.xixi.spring.cloud10.server.domain.User;
import com.xixi.spring.cloud10.server.fallback.UserServiceFallback;
import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**被标记为@FeignClient的接口,将会伪装UserService的客户端去调用它要调用的服务
* 用户服务
* 声明Feign客户端,feign client里面的方法将会映射到rest api
* ${user-service-name}指向user-service-provider
*
* 整合Hystrix Fallback
*/
@FeignClient(name = "${user.service.name}",//利用占位符,避免未来整合硬编码
primary = true,//primary = true表示当UserService有多个实现类的时候,FeignClient动态代理的那个实现类优先级最高
fallback = UserServiceFallback.class )
public interface UserService {
/**
* 保存用户
* @param user
* @return
*/
@PostMapping("/user/save")
boolean saveUser(@RequestBody User user);
/**
* 查询所有的用户列表
* @return
*/
@GetMapping("/user/list")
List<User> findAll();
}
写一个UserServiceFallback
package com.xixi.spring.cloud10.server.fallback;
import com.xixi.spring.cloud10.server.api.UserService;
import com.xixi.spring.cloud10.server.domain.User;
import java.util.Collections;
import java.util.List;
public class UserServiceFallback implements UserService {
@Override
public boolean saveUser(User user) {
return false;
}
@Override
public List<User> findAll() {
return Collections.emptyList();
}
}
2,在user-service-provider上整合@HystrixCommand
@HystrixCommand(commandProperties = {
//设置超时时间为100ms
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")
},
//通过@HystrixCommand#fallbackMethod()实现异常处理,设置fallback方法,会在当前类或是他的父类里面去找fallbackForGetUsers()
fallbackMethod = "fallbackForGetUsers")
public List<User> findAll() {
return userService.findAll();
}
九、• 整合服务发现: Netflix Eureka
(废弃ribbon白名单配置的方式,使用eureka获取服务列表)
1,写一个eureka-server
(1)引入eureka-server jar
(2)写引导类
(3)配置文件application.properties
2,user-service-client增加服务发现配置
(1)加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)引导类激活服务发现@EnableDiscoveryClient
(3)application.properties,注意要注释掉之前白名单的配置
eureka.client.enabled = true
###连接eureka server
eureka.server.hostname = localhost
eureka.server.port = 8083
eureka.client.serviceUrl.defaultZone = \
http://${eureka.server.hostname}:${eureka.server.port}/eureka
3,user-service-provider增加服务发现配置
(1)加依赖
(2)引导类加注解@EnableDiscoveryClient
(3)application.properties
十、• 整合配置服务器: Config Server
(1)加依赖
s<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
(2)引导类加注解@EnableDiscoveryClient,@SpringBootApplication,@EnableConfigServer
(3)application.properties
1,配置user-service.properties
###user-service-client配置内容,作为user-service-client配置内容的一部分,
# user-service-client本身也会有自己的application.properties
##如果有冲突,user-service的配置内容优先
##服务提供方名称
provider.service.name = user-service-provider
##服务提供方主机
#provider.service.host = localhost
####服务提供方端口
#provider.service.port = 8081
###配置@FeignClient(name = "${user.service.name}")中的占位符
#user.service.name需要制定user service接口的提供方,也就是user-service-provider
user.service.name = ${provider.service.name}
2,
3,
十一、• 整合配置客户端: Config Client