1、概念java
基於springboot基礎上用於快速構建分佈式系統的通用模式的工具集。web
2、特色spring
一、約定優於配置;springboot
二、隱藏組件複雜性;架構
三、輕量級組件;app
四、組件豐富,功能齊全,例如:服務發現、斷路器、微服務網關等;dom
五、選型中立、豐富;分佈式
六、靈活。ide
3、服務消費者與服務提供者微服務
在微服務架構中有兩種角色:服務消費者與服務提供者,兩者關係以下:
服務提供者:服務的被調用者
服務消費者:服務的調用者
例如,在電影系統中,用戶購買電影票票以前,電影服務須要調用用戶服務的接口獲取用戶信息,此時的電影服務就是調用方,即服務消費者,用戶服務爲被調用方,即服務提供者。
4、微服務實踐
一、服務提供者:用戶服務
項目結構以下:
這個demo主要是爲了演示服務與服務之間的通訊,所以再也不配置數據源。
User.java
package com.my.user.entity; import lombok.Data; /** * @author 垃圾美少女 */ @Data public class User { private Integer id; private String name; private Integer age; private String username; private Integer balance; }
IUserService.java
package com.my.user.service; import com.my.user.entity.User; /** * @author 垃圾美少女 */ public interface IUserService { User getByUserId(Integer userId); }
UserServiceImpl.java
package com.my.user.service.impl; import com.my.user.entity.User; import com.my.user.service.IUserService; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * @author 垃圾美少女 */ @Service public class UserServiceImpl implements IUserService { @Override public User getByUserId(Integer userId) { List<User> userList = getUserList(); userList = userList.stream() .filter(user -> Objects.equals(user.getId(), userId)) .collect(Collectors.toList()); if (userList != null && userList.size() > 0) { return userList.get(0); } return null; } /** * 因爲沒有配置數據源,在此設置虛擬數據 * * @return list */ private List<User> getUserList() { List<User> list = new ArrayList<>(5); for (int i = 0; i < 5; i++) { User user = new User(); user.setAge(12 + 1); user.setId(1 + i); user.setName("用戶" + i); user.setUsername("用戶名" + i); user.setBalance(123 + i); list.add(user); } return list; } }
UserController.java
package com.my.user.controller; import com.my.user.Util.ReturnUtil; import com.my.user.entity.User; import com.my.user.service.IUserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * @author 垃圾美少女 */ @RestController @Slf4j public class UserController { @Autowired private IUserService userService; /** * 根據id獲取用戶信息 * * @param userId 用戶id * @return map */ @RequestMapping(value = "/user/getUserInfo", method = RequestMethod.GET) public Map getUserInfo(Integer userId) { try { log.info("/user/getUserInfo被訪問,參數:userId=" + userId); User user = userService.getByUserId(userId); return ReturnUtil.succe***esult(user, "獲取成功"); } catch (Exception e) { log.error(e.getMessage(), e); return ReturnUtil.errorResult(null, "獲取失敗"); } } }
ReturnUtil.java
package com.my.user.Util; import java.util.HashMap; import java.util.Map; /** * @author 垃圾美少女 */ public class ReturnUtil { public static Map succe***esult(Object data, String msg) { Map<String, Object> map = new HashMap<>(3); map.put("code", 1); map.put("msg", msg); map.put("data", data); return map; } public static Map errorResult(Object data, String msg) { Map<String, Object> map = new HashMap<>(3); map.put("code", -1); map.put("msg", msg); map.put("data", data); return map; } }
application.yml
server: port: 8010 #指定端口爲 8010
啓動項目後訪問:http://localhost:8010/user/getUserInfo?userId=1
獲得相應:
{ "msg": "獲取成功", "data": { "id": 1, "name": "用戶0", "age": 13, "username": "用戶名0", "balance": 123 }, "code": 1 }
表示接口已通。
二、服務消費者:電影服務
項目架構以下:
User.java和ReturnUtil.java與上例相同在此再也不展現。
MovieApplicaiton.java
package com.my.movie; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class MovieApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(MovieApplication.class, args); } }
MovieController.java
package com.my.movie.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; /** * @author 垃圾美少女 */ @RestController @Slf4j public class MovieController { @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/movie/findById", method = RequestMethod.GET) public Map findById(Integer userId) { log.info("/movie/findById被訪問,參數:userId=" + userId); ResponseEntity<HashMap> forEntity = this.restTemplate.getForEntity("http://localhost:8010/user/getUserInfo?userId=" + userId, HashMap.class); return forEntity.getBody(); } }
application.yml
server: port: 8020
此時啓動項目,訪問:http://localhost:8020/movie/findById?userId=1
獲得響應:
{ "msg": "獲取成功", "code": 1, "data": { "id": 1, "name": "用戶0", "age": 13, "username": "用戶名0", "balance": 123 } }
至此,一個簡單的電影微服務就完成了。
5、上述例子中存在的問題
一、在代碼中寫死訪問路徑
在電影服務中,能夠將user服務的訪問路徑寫到yml配置文件中,使代碼更清爽:
yml:
server: port: 8020 userService: domain: http://localhost:8010/user/ getUserByIdUrl: http://localhost:8010/user/getUserInfo?userId=
MovieController.java
package com.my.movie.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; /** * @author 垃圾美少女 */ @RestController @Slf4j public class MovieController { @Autowired private RestTemplate restTemplate; @Value("${userService.domain}") private String userServiceDomain; @Value("${userService.getUserByIdUrl}") private String findByUserIdUrl; @RequestMapping(value = "/movie/findById", method = RequestMethod.GET) public Map findById(Integer userId) { log.info("/movie/findById被訪問,參數:userId=" + userId); ResponseEntity<HashMap> forEntity = this.restTemplate.getForEntity(findByUserIdUrl + userId, HashMap.class); return forEntity.getBody(); } }
二、適用場景有限:當用戶服務的地址或端口號發生改變時,須要修改電影服務的配置文件而且從新部署,這顯然是不可取的。