一、服務調用
服務調用和消息一般是兩個維度初婆,
1,服務調用是同步的
2猿棉,消息是異步的回調磅叛,事件的一個周期
二、準備
? 遠程過程調用(RPC)
? 接口定義語言(IDL)
interface definition language
? 通訊協(xié)議(Protocol)
HTTP萨赁,web-socket弊琴,Socket
? Netflix Feign
契約式編程
springcloud將Feign和spring mvc整合
? Spring Cloud 技術回顧 (服務短路 、負載均衡杖爽、服務發(fā)現(xiàn)访雪、分布式配置等)
三、
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脂倦,通過網(wǎng)絡進行傳輸。
一個計算機通信協(xié)議元莫。該協(xié)議允許運行于一臺計算機的程序調用另一臺計算機的子程序(進程)赖阻,而程序員無需額外地為這個交互作用編程。如果涉及的軟件采用面向對象編程踱蠢,那么遠程過程調用亦可稱作遠程調用或遠程方法調用火欧。
? 例如
? Java RMI(二進制協(xié)議)進程之間的協(xié)議棋电,必須用POJO(序列化&反序列化)
? WebServices(文本協(xié)議)一般都有骨架或者說是結構,比如XML schema
2苇侵,消息傳遞
RPC 是一種請求-響應協(xié)議赶盔,一次 RPC在客戶端初始化,再由客戶端將請求消息傳遞到遠程的服務器榆浓,執(zhí)行指定的帶有參數(shù)(這里的參數(shù)和方法的參數(shù)有所區(qū)別于未。這里的參數(shù)也有可能是請求頭之類的信息)的過程。經(jīng)過遠程服務器執(zhí)行過程后陡鹃,將結果作為響應內容(響應頭和響應體)返回到客戶端烘浦。
3,存根(Stub)
在一次分布式計算 RPC 中萍鲸,客戶端和服務器轉化參數(shù)(不僅僅是方法的參數(shù))的一段代碼谎倔。 由于存根的參數(shù)轉化,RPC執(zhí)行過程如同本地執(zhí)行函數(shù)調用猿推。存根必須在客戶端和服務器兩端均裝載片习,并且保持兼容(比如高版本的服務端兼容低版本的客戶端調用)。
HTTP調用的存根在于內部的實現(xiàn)蹬叭。web服務器和客戶端都支持HTTP協(xié)議的話藕咏,那么協(xié)議的內容和格式就是存根。
如果是java rmi(remote method invoke)的話秽五,兩邊都必須要有接口孽查,傳輸?shù)哪P蛯ο蟊仨毿蛄谢惖陌姹疽3忠恢绿勾琾ojo的字段也要兼容盲再。
java 序列化主要關注POJO字段的狀態(tài),對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
如果想自己擴展念赶,可以實現(xiàn)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}
##配置自定義實現(xiàn)的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標記的接口可以直接注入,F(xiàn)eign會動態(tài)代理幫我們去構造它的實現(xiàn)類
*/
@Autowired
private UserService userServiceFeignClient;
@GetMapping("/user/list")
public List<User> getUserList2() {
return userServiceFeignClient.findAll();
}
3回还,服務端實現(xiàn)user-service API裆泳,暴露Rest接口
首先,
@Service("inMemoryUserService")
public class InMemoryUserService implements UserService
其次柠硕,
user-service-provider是最終的服務提供方工禾,不需要標記為feignClient
UserServiceProviderController implements UserService
UserServiceProviderController實現(xiàn)feignclient接口。
最后蝗柔,
//由于UserServiceProviderController繼承了UserService闻葵,所以這塊可以不寫@PostMapping("/user/save")。Feign Inheritance Support
//注意:@PostMapping會被繼承癣丧,但是@RequestBody槽畔,@RequestParam是不會被繼承的,method parameter mapping is not inherited
//這是因為java語言的特性決定的胁编,方法外面的東西可以從父類繼承厢钧,但是方法里面的實現(xiàn),子類會覆蓋父類嬉橙。
//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有多個實現(xiàn)類的時候拾给,F(xiàn)eignClient動態(tài)代理的那個實現(xiàn)類優(yōu)先級最高
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()實現(xiàn)異常處理,設置fallback方法,會在當前類或是他的父類里面去找fallbackForGetUsers()
fallbackMethod = "fallbackForGetUsers")
public List<User> findAll() {
return userService.findAll();
}
九、? 整合服務發(fā)現(xiàn): Netflix Eureka
(廢棄ribbon白名單配置的方式蒋得,使用eureka獲取服務列表)
1级及,寫一個eureka-server
(1)引入eureka-server jar
(2)寫引導類
(3)配置文件application.properties
2,user-service-client增加服務發(fā)現(xiàn)配置
(1)加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)引導類激活服務發(fā)現(xiàn)@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增加服務發(fā)現(xiàn)配置
(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的配置內容優(yōu)先
##服務提供方名稱
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