上一節咱們已經學習了Eureka 註冊中心,其實咱們也使用到了Ribbon ,只是當時咱們沒有細講,因此咱們如今一塊兒來學習一下Ribbon。html
以前接觸到的負載均衡都是硬負載均衡,什麼是硬負載均衡呢?硬負載均衡就是在以往的大型系統中,會有單獨一套系統來負責負載均衡策略,咱們因此的請求都會先走到負載均衡的系統上,進行分配到不一樣的服務器處理。
好比咱們熟悉的nginx 。其實就能夠算做一個負載均衡的系統,客戶端請求的接口會先經過nginx 的負載均衡策略分配到不一樣的服務器上。
那Ribbon 不是這樣的嗎?那又是怎樣的呢?
Ribbon 是和 Eureka 同樣是Netflix 推出的開源產品,它能夠和Eureka 完成無縫結合,Ribbon 主要實現客戶端負載均衡。
那什麼是客戶端負載均衡呢?
就是在客戶端請求的時候,就經過均衡策略將請求分發到不一樣的服務器上,以下圖
這個圖是根據上節的Eureka 的架構圖改編來的,主要的流程仍是沒有變,服務消費者和服務提供者都會註冊到服務中心,而後服務消費者會從服務中心獲取可用實例列表 ,這裏就會經過負載均衡策略選擇其中一個實例進行訪問。java
好了咱們來用代碼來看一下。linux
咱們爲了簡化,註冊中心服務端,咱們仍是用上節的單節點。怎麼配置我不說了。而後咱們新建一個module 用來作服務提供者,其實也能夠用上一節的服務提供者,可是我怕揉在一塊兒很差,因此就全都分開了,不過不少代碼都是同樣的。nginx
咱們新建一個模塊後pom.xml 文件以下:git
<parent> <groupId>cn.quellanan</groupId> <artifactId>SpringCloud</artifactId> <version>1.0.0</version> </parent> <groupId>com.quellanan.springcloud</groupId> <artifactId>ribbon-provider-9004</artifactId> <version>1.0.0</version> <name>ribbon-provider-9004</name> <description>ribbon-provider-9004 服務提供者</description> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
主要就是引入了eureka-client 能夠註冊到註冊中心去,而後啓動類加上@EnableEurekaClient 註解
在配置文件中加上配置以下:程序員
server.port=9004 spring.application.name=ribbon-provider eureka.client.service-url.defaultZone=http://localhost:8000/eureka/
咱們在建立一個測試類:HelloControllergithub
@RestController @Slf4j public class HelloController { @Value("${server.port}") private String port; @RequestMapping("/hello") public String hello(){ log.info(port); return "hello "+port; } }
這樣咱們一個服務提供者就弄好了,爲了看出負載均衡的效果,咱們還須要建立一個同樣的服務提供者。或者不一樣的端口啓動。咱們將端口改成9005.其餘的和上面同樣。web
服務提供者有了,咱們再來建立一個服務消費者的模塊。pom.xml 較服務提供者就多了一個ribbon 的依賴spring
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
而後啓動類中加上@EnableEurekaClient 和 RestTemplatejson
@SpringBootApplication @EnableEurekaClient public class RibbonConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonConsumerApplication.class, args); } }
@LoadBalanced 註解就是來實現客戶端負載均衡的。
配置文件中加入以下配置:
server.port=9003 #服務名,在註冊時所用 spring.application.name=ribbon-consumer eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
最後咱們來寫一個調用服務提供者的接口,建立一個IndexController類,內容以下
@RestController public class IndexController { private static final String applicationName = "ribbon-provider"; @Autowired private RestTemplate restTemplate; @RequestMapping("/index") public String getHello(){ String url = "http://"+ applicationName +"/hello"; return restTemplate.getForObject(url,String.class); } }
咱們如今來啓動服務中心,兩個服務提供者,一個服務消費者。啓動以後咱們輸入:
http://localhost:8000/
重點看下ribbon-provider 有兩個端口,分別對應的咱們的兩個服務提供者。他們的appliaction.name是相同的。
再來調下面接口看看
http://localhost:9003/index
能夠看到每次調用訪問了不一樣的服務提供者,兩個服務端提供者是輪尋調用的。從而實現客戶端的負載均衡
上面說的負載均衡,其實仍是RestTemplate 對象加上@LoadBalanced來實現的。而且前面只是簡單的調用,沒有涉及參數和請求方式,接下來咱們看看常見的請求方式和有參數的調用。
其實咱們以前寫的就是get 請求的方式,咱們在來寫一個有參數的請求
@RequestMapping("index2") public String getHello2(){ String url = "http://"+ applicationName +"/hello2?name={1}"; return restTemplate.getForObject(url,String.class,"quellanan"); }
能夠看到url 中請求的參數有佔位符代替,getForObject或者getForEntity的第三個參數就是咱們實際傳的參數。這裏說明一下getForObject 是直接獲取返回的內容,而getForEntity返回的是一個http對象,包含相應狀態碼,想要回去內容須要getForEntity().getBody() 才行。
那若是多個參數的呢?
多個參數的經常使用的有兩種方式,一個是和上面同樣,直接在後面加參數就行了以下:
@RequestMapping("index3") public String getHello3(){ //多個參數拼接 String url = "http://"+ applicationName +"/hello3?name={1}&age={2}"; return restTemplate.getForObject(url,String.class,"quellanan","18"); }
還有一種方式就是將參數封裝到map 中,傳過去。同樣的也能夠解析
@RequestMapping("index4") public String getHello4(){ //多參數組裝 Map<String,String> parms=new HashMap<>(); parms.put("name","quellanan"); parms.put("age","18"); String url = "http://"+ applicationName +"/hello3?name={name}&age={age}"; return restTemplate.getForObject(url,String.class,parms); }
咱們在提供者中寫兩個方法便於測試
@RequestMapping("/hello2") public String hello2(@RequestParam("name") String name){ log.info(name); return "hello "+name+port; } @RequestMapping("/hello3") public String hello3(@RequestParam("name") String name,@RequestParam("age") String age){ log.info(name+age); return "hello "+name+age+port; }
咱們啓動來看下結果
能夠看到參數是傳遞成功的啦。
post 請求和get 請求差很少,我這裏就將參數封裝到map中了
postForEntity
@RequestMapping("index6") public String getHello6(){ //postForEntity JSONObject params=new JSONObject(); params.put("name","quellanan"); params.put("age","18"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity(params.toJSONString(), headers); String url = "http://"+ applicationName +"/hello4"; return restTemplate.postForEntity(url,request,String.class).getBody(); }
postForObject
@RequestMapping("index7") public String getHello7(){ //postForObject JSONObject params=new JSONObject(); params.put("name","quellanan"); params.put("age","18"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity(params.toJSONString(), headers); String url = "http://"+ applicationName +"/hello4"; return restTemplate.postForObject(url,params,String.class); }
主要是先將參數封裝在JSONObject 中,而後設置HttpHeaders 和HttpEntity ,而後請求。
咱們採用的application/json 的格式,咱們在服務提供者中加一個方法。用來接收json格式的參數。
@RequestMapping("/hello4") public String hello4(@RequestBody Map<String, Object> parms){ return "hello "+parms.get("name")+parms.get("age")+port; }
如今咱們啓動看下效果。
證實都是能夠正常傳遞的。
上面咱們能夠看到,咱們進行微服務調用,不論是使用get 或者post 方法,帶有參數過多就會致使代碼變得很臃腫,因此咱們就可使用一樣是netflix 推出的Feign 來簡化微服務調用,Feign 結合了Ribbon 以及Hystrix.Ribbon的功能它都有,而且還進行了封裝,更加方便咱們使用。因此咱們使用的時候,只用引入 Feign 的依賴就能夠。
那咱們如今對上面的這些進行調整一下。
在咱們的ribbon-consumer 的pom 文件中刪除ribbon 的依賴,增長feign 的依賴。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
在啓動類中增長@EnableFeignClients 註解
在啓動類中增長了@EnableFeignClients 註解,就能夠在項目中使用Fegin 啦,那咱們怎麼使用呢?
其實還要使用@FeignClient 註解,這個註解是在具體的應用中注入的,用來指定服務提供者的服務名稱。而且這個註解只能做用 interface 上。
前面咱們調用服務提供者的接口須要寫url,參數,返回類型等等很是的繁瑣,因此Fegin 就幫咱們進行了簡化,讓咱們調用服務提供者的接口,能夠像自身調用同樣,很是放方便。
咱們來建立一個HelloService 的接口。內容以下
@FeignClient("ribbon-provider") public interface HelloService { @RequestMapping("/hello") public String hello(); @RequestMapping("/hello2") public String hello2(@RequestParam(value = "name") String name); @RequestMapping("/hello3") public String hello3(@RequestParam(value = "name") String name,@RequestParam(value = "age") String age); @RequestMapping("/hello4") public String hello4(@RequestBody Map<String, Object> parms); }
能夠看到,上面的接口內容主要是針對服務提供者暴露出的幾個接口進行調用。
對比服務消費者中的接口,和服務提供者中的接口,能夠發現實際上是服務提供者中的接口對消費者中的HelloService 的實現。這個待會再說。從HelloService 中咱們能夠看到,咱們調用服務提供者的接口同樣的採用@RequestMapping,@RequestParam,@RequestBody來操做,這樣更加簡潔和方便。
咱們再建立一個FeginController 來測試一下,內容以下:
@RestController public class FeginController { @Autowired public HelloService helloService; @RequestMapping("/fegin") public String getHello(){ return helloService.hello(); } @RequestMapping("/fegin2") public String getHello2(){ String name="quellanan"; return helloService.hello2(name); } @RequestMapping("/fegin3") public String getHello3(){ String name="quellanan"; String age="18"; return helloService.hello3(name,age); } @RequestMapping("/fegin4") public String getHello4(){ Map<String, Object> parms=new HashMap<>(); parms.put("name","quellanan"); parms.put("age","18"); return helloService.hello4(parms); } }
能夠看到就是普通的controller層調用service 層。
好了咱們如今來測試一下。
分別輸入以下地址來看看效果:
http://localhost:9003/fegin http://localhost:9003/fegin2 http://localhost:9003/fegin3 http://localhost:9003/fegin4
好了,來講最後一個問題,剛剛上面咱們說了服務消費者中的HelloService 和服務提供者的HelloController很像,感受像是HelloController 實現了HelloService 。不錯,當咱們正式開發的時候,會發現接口調用很是多,而且也很複雜,若是按照上面的方式來的話,會存在不少的重複代碼且很容易出錯,因此咱們能夠將服務調用單獨提取成一個模塊麼,而後分別在服務提供者和服務消費者中引入其依賴,而後在消費中的HelloService 繼承其對應的接口。而在服務提供者中實現其對應的接口。固然@FeignClient仍是在服務消費者之中的。
這裏只是提供了一種思路,沒有給出實現方式,感興趣的能夠看看《Spring cloud 微服務實戰》,也能夠和我討論下嘿嘿。
總算是寫完了,算是對ribbon 和fegin 有了一些瞭解,最起碼如今可使用他們,並將其使用到到項目中沒有什麼問題啦。
代碼上傳到github:
https://github.com/QuellanAn/springcloud
後續加油♡
很榮幸,今年參加了CSDN博客之星評選活動,幫忙投下票,能夠投5票 ,謝謝您
最後啦,
歡迎你們關注我的公衆號 "程序員愛酸奶"
分享各類學習資料,包含java,linux,大數據等。資料包含視頻文檔以及源碼,同時分享本人及投遞的優質技術博文。
若是你們喜歡記得關注和分享喲❤