一個最簡單的微服務架構
前言
微服務架構一般會有一個開放網關作為總入口,負責分發流量到實際的應用服務上。下面看圖。
架構圖
項目結構
這個架構分別由反向代理nginx,註冊中心zookeeper,開放網關gateway,和兩個服務goodservice,priceservice組件而成。為了方便測試,我把建了兩個一樣的gateway和goodservice。而common作為公共的二方包存在,也是為了簡單起見,gateway和service引用同一個二方包。
nginx
nginx除了作為反向代理,也具有負載均衡的功能,默認策略是輪詢地址列表。我這裏設置8080和8081兩個端口,如下
upstream mygateway { server localhost:8080; server localhost:8081; } server { listen 80; server_name localhost; location /route{ proxy_pass http://mygateway; } }
priceservice
非常簡單的服務,只提供一個價格查詢的接口,如
@Service public class PriceServiceImpl implements PriceService { @Override public Integer getGoodPrice(String name) { return 100; } }
goodservice
也只提供一個接口,但是依賴priceservice,如
@Service public class GoodServiceImpl implements GoodService { private final String SERVICENAME="goodservice"; @Reference(check=false) PriceService priceService; @Override public List<GoodInfo> getGoodList(String name) { List<GoodInfo> goodInfoList=new ArrayList<>(); GoodInfo goodInfo=new GoodInfo(); goodInfo.setName(name); goodInfo.setDescription(SERVICENAME); Integer price= priceService.getGoodPrice(name); goodInfo.setPrice(price); goodInfoList.add(goodInfo); return goodInfoList; } }
gateway
gateway這裏的作用是提供一個統一對外的接口,當然還可以加上鑒權,限流,防黑,監控等功能。實現上是通過dubbo的泛化調用將流量通過負載均衡策略轉到實際的應用中,均衡策略默認是隨機。dubbo的泛化調用是需要去匹配對應接口的方法名和參數類型。正常情況下,是需要通過api註冊和管理錄入到數據庫,再提供給gateway使用的。我這裏通過靜態塊構造一些數據充當api註冊。如下
@RestController public class RouteController { private final static List<ServiceModel> serviceModels = new ArrayList<>(); private final static Map<String,GenericService> genericServiceMap=new HashMap<>(); private final static String GATEWAYNAME="gateway"; static { ParameterModel parameterModel = new ParameterModel(); parameterModel.setName("name"); parameterModel.setType("java.lang.String"); List<ParameterModel> parameterModelList = new ArrayList<>(); parameterModelList.add(parameterModel); ServiceModel serviceModel = new ServiceModel(); serviceModel.setApiName("api.service.goodservice"); serviceModel.setServiceName("com.example.demo.common.service.GoodService"); serviceModel.setMethodName("getGoodList"); serviceModel.setParameterModels(parameterModelList); serviceModels.add(serviceModel); } @RequestMapping(value = "/route", method = RequestMethod.GET) public ResultModel execute(@RequestParam String api, @RequestParam String data) { Optional<ServiceModel> serviceModelOptional = serviceModels.stream().filter(x -> x.getApiName().equals(api)).findFirst(); ResultModel resultModel=new ResultModel(); if (!serviceModelOptional.isPresent()) { resultModel.setDescription("api不存在"); } ServiceModel serviceModel=serviceModelOptional.get(); GenericService genericService= genericServiceMap.get(api); if(genericService==null){ ReferenceConfig<GenericService> reference = new ReferenceConfig<>(); reference.setInterface(serviceModel.getServiceName()); reference.setGeneric(true); genericService = reference.get(); genericServiceMap.put(api,genericService); } Object result = genericService.$invoke(serviceModel.getMethodName(),getTypeList(serviceModel).toArray(new String[]{}), dataToValueList(serviceModel,data).toArray()); resultModel.setData(result); resultModel.setDescription(GATEWAYNAME); return resultModel; } /** * 獲取參數類型列表 * @param serviceModel * @return */ private List<String> getTypeList(ServiceModel serviceModel) { List<ParameterModel> parameterModelList = serviceModel.getParameterModels(); if (CollectionUtils.isEmpty(parameterModelList)) { return null; } return parameterModelList.stream().map(x -> x.getType()).collect(Collectors.toList()); } /** * 獲取data中的值列表 * @param serviceModel * @param data * @return */ private List<Object> dataToValueList(ServiceModel serviceModel, String data) { Map<String, Object> parameterMap = jsonToMap(data); List<ParameterModel> parameterModelList = serviceModel.getParameterModels(); if (CollectionUtils.isEmpty(parameterModelList)) { return null; } List<Object> valueList = new ArrayList<>(); parameterModelList.stream().forEach(x -> { valueList.add(parameterMap.get(x.getName())); }); return valueList; } /** * 將map格式的string轉成map對象 * @param json * @return */ public static Map<String, Object> jsonToMap(String json) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(json, Map.class); } catch (IOException e) { System.out.println(e); } return null; } }
測試
接下來,方便起見,只需要在一臺電腦把幾個module全部啟動起來,在瀏覽器輸入
http://mygateway/route?api=api.service.goodservice&data={"name":"蘋果"}
多測試幾遍,會看到返回如下
{"data":[{"price":100,"name":"蘋果","description":"goodservice","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway1"} {"data":[{"price":100,"name":"蘋果","description":"goodservice1","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway"} {"data":[{"price":100,"name":"蘋果","description":"goodservice","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway"} {"data":[{"price":100,"name":"蘋果","description":"goodservice1","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway1"}
實際上整個調用鏈路會在gateway集群和goodservice集群交叉流轉,如果這個時候把goodservice或者gateway停掉,瀏覽器的調用還是會正常返回的,這就是集群的好處。
但是如果這個時候把停掉nginx,zookeeper和priceservice其中一個,瀏覽器將會調用失敗,因為是單點的。在生產環境中,要保證高可用,架構上是不可以出現單點的應用。
小結
上面的架構只是微服務架構中的一種形態。阿裏內部在這方面做得更極致一點,直接將gateway這層去掉,而是作為一個二方包集成到業務應用中,由接入層直接轉發流量。簡單來說,就是這樣的。終端->LVS集群->Aserver集群(nginx的加強版)->應用服務。
git地址: https://github.com/mycaizilin/microservice
一個最簡單的微服務架構