The target

  • This section describes the underlying principles, load balancing, and IP port probing of divide plug-in
  • Load balancing principle analysis
  • IP port detection port analysis
  • conclusion

Divide Plug-in basic principles, load balancing, IP port detection

The Divide plug-in is the core processing plug-in for the gateway to process HTTP requests. The Divide plug-in implements HTTP forward proxy. All HTTP requests are load balanced by the plug-in. Load balancing is where The Divide plug-in forwards requests to a specific back-end machine based on the responsible balancing algorithm. Soul-admin provides IP + port detection. The soul-admin has a scheduled task to scan the configured IP port and remove the IP + port if it is found offline.

In the previous article, WE explained how an HTTP request is propped up by a gateway. In that article, we explained how HTTP requests are handled by Divide and the source code of Divide. We also explained the principle (chain of responsibility).

Therefore, this article further discusses load balancing and IP port probing

Soul gateway load balancing source code analysis

First, let’s take a look at the Divide plug-in load balancing module and an overview of each module

  • LoadBalance interface

This is an SPI interface, so the Load balancing implementation of the Soul gateway is extensible, allowing developers to implement load balancing based on their own business needs and system conditions. This interface provides a select method that returns a DivideUpstream object containing the following properties:

  • UpstreamHost: live IP
  • Protocol: the agreement
  • UpstreamUrl: Request address
  • Weight: the weight
  • Status: indicates the status. The default value is true
  • Timestamp: indicates the timestamp
  • Heat warmup:
@SPI
public interface LoadBalance {

    /**
     * this is select one for upstream list.
     *
     * @param upstreamList upstream list
     * @param ip ip
     * @return divide upstream
     */
    DivideUpstream select(List<DivideUpstream> upstreamList, String ip);
}
Copy the code
  • AbstractLoadBalance class

This class is the base class of the load balancing algorithm supported by the Soul gateway. It implements the LoadBalance interface, overwrites the SELECT method, and provides the abstract method doSelect. The concrete implementation class of the load balancing will overwrite the doSelect method, and the doSelect method is the implementation of the concrete load balancing algorithm. It also provides a getWeight method, and pays special attention to the calculateWarmupWeight method, which calculates the actual weight based on the time difference, interface heat, and weight.

  • HashLoadBalance

Hash algorithm of load balancing implementation

  • RandomLoadBalance

Random algorithm of load balancing implementation

  • RoundRobinLoadBalance

Polling algorithm of load balancing implementation, this piece

  • conclusion

Divide is a load balancing plugin designed for the Soul gateway. Divide is a load balancing plugin designed for the Soul gateway. Divide is a load balancing plugin designed for divide. It is to divide the plugin for load balancing the DivideUpstream DivideUpstream = LoadBalanceUtils. The selector (upstreamList, ruleHandle.getLoadBalance(), ip);

IP port probing for The Soul gateway Divide plug-in

There is a scheduled task in soul-admin that scans for IP ports and removes the IP + port if it goes offline. Not only is there a scheduled task in soul-admin, there is also a scheduled task in soul-admin for IP + ports

Whether IP port probing is enabled on the soul-admin port and the probing frequency can be configured. If not, the checking frequency is 10 seconds by default. The configuration is as follows:

Soul. Upstream. Check: true is true by default, set to false, no soul. Upstream. ScheduledTime: 10Copy the code

Soul gateway side is divide plug-in handler (DividePluginDataHandler) initialization of initialization, you can refer to this code DividePluginConfiguration, Look at the DividePluginDataHandler here

Let’s take a look at the soul-admin side and the Soul gateway side

  • Soul – the admin side

On the soul-admin side, the UpstreamCheckService is used to perform the search. The @postConstruct annotation can be seen in this code. Constructor >> @autoWired >> @postConstruct. The following code is UpstreamCheckService, which handles the details by referring to the comment

@postconstruct public void setup() {// Get separate PluginDO PluginDO = pluginMapper.selectByName(PluginEnum.DIVIDE.getName()); if (pluginDO ! = null) { List<SelectorDO> selectorDOList = selectorMapper.findByPluginId(pluginDO.getId()); for (SelectorDO selectorDO : selectorDOList) { List<DivideUpstream> divideUpstreams = GsonUtils.getInstance().fromList(selectorDO.getHandle(), DivideUpstream.class); If (CollectionUtils isNotEmpty (divideUpstreams)) {/ / initialized to live content of the selector, UPSTREAM_MAP is a global map upstream_map. put(selectordo.getName (), divideUpstreams); Check if (check) {// create a thread pool for timed processing, The new thread pool scheduled execution method ScheduledThreadPoolExecutor (Runtime. The getRuntime (). AvailableProcessors (), SoulThreadFactory.create("scheduled-upstream-task", false)) .scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS); }}Copy the code
private void check(final String selectorName, final List<DivideUpstream> upstreamList) { List<DivideUpstream> successList = Lists.newArrayListWithCapacity(upstreamList.size()); for (DivideUpstream divideUpstream : UpstreamList) {/ / based on the URL, IP + port agent live final Boolean pass = UpstreamCheckUtils. CheckUrl (divideUpstream. GetUpstreamUrl ()); If (pass) {// If the state is false, update the state if (! divideUpstream.isStatus()) { divideUpstream.setTimestamp(System.currentTimeMillis()); divideUpstream.setStatus(true); log.info("UpstreamCacheManager check success the url: {}, host: {} ", divideUpstream.getUpstreamUrl(), divideUpstream.getUpstreamHost()); } // Save the live plug-in successlist.add (divideUpstream) to the live list; Divideuppage.setstatus (false);} else {// Set divideuppage.setStatus (false); log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl()); } } if (successList.size() == upstreamList.size()) { return; } if (successList.size() > 0) {upstream_map. put(selectorName, successList); // Update the selector updateSelectorHandler(selectorName, successList); } else {// Remove the active content upstream_map. remove(selectorName); updateSelectorHandler(selectorName, null); }}Copy the code
private void updateSelectorHandler(final String selectorName, final List<DivideUpstream> upstreams) { SelectorDO selector = selectorService.findByName(selectorName); if (Objects.nonNull(selector)) { SelectorData selectorData = selectorService.buildByName(selectorName); if (upstreams == null) { selector.setHandle(""); selectorData.setHandle(""); } else { String handler = GsonUtils.getInstance().toJson(upstreams); selector.setHandle(handler); selectorData.setHandle(handler); } / / update the database data selectorMapper updateSelective (selector); / / release data change notification eventPublisher. PublishEvent (new DataChangedEvent (ConfigGroupEnum. The SELECTOR, DataEventTypeEnum. UPDATE, Collections.singletonList(selectorData))); }}Copy the code
  • Soul gateway side

This class is a singleton, initialized as an action of the DividePluginDataHandler. The code handling logic is similar to that of the soul-admin side.

@slf4J public Final Class UpstreamCacheManager {// UpstreamCacheManager instance initializes private static Final UpstreamCacheManager INSTANCE = new UpstreamCacheManager(); private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP = Maps.newConcurrentMap(); private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP_TEMP = Maps.newConcurrentMap(); /** * constructor, Initialize the scheduled thread pool to perform scheduled */ private UpstreamCacheManager() {Boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false")); if (check) { new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false)) .scheduleWithFixedDelay(this::scheduled, 30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS); }} /** * Return a singleton UpstreamCacheManager INSTANCE */ public static UpstreamCacheManager getInstance() {return INSTANCE; } /** * according to the selector ID, Acquire the selector data to explore the living * / public List < DivideUpstream > findUpstreamListBySelectorId (final String selectorId) {return UPSTREAM_MAP_TEMP.get(selectorId); } /** * public void removeByKey(final String key) {upstream_map_temp. remove(key); Public void submit(final SelectorData SelectorData) {final List<DivideUpstream> upstreamList = GsonUtils.getInstance().fromList(selectorData.getHandle(), DivideUpstream.class); if (null ! = upstreamList && upstreamList.size() > 0) { UPSTREAM_MAP.put(selectorData.getId(), upstreamList); UPSTREAM_MAP_TEMP.put(selectorData.getId(), upstreamList); } else { UPSTREAM_MAP.remove(selectorData.getId()); UPSTREAM_MAP_TEMP.remove(selectorData.getId()); } } private void scheduled() { if (UPSTREAM_MAP.size() > 0) { UPSTREAM_MAP.forEach((k, v) -> { List<DivideUpstream> result = check(v); if (result.size() > 0) { UPSTREAM_MAP_TEMP.put(k, result); } else { UPSTREAM_MAP_TEMP.remove(k); }}); } } private List<DivideUpstream> check(final List<DivideUpstream> upstreamList) { List<DivideUpstream> resultList = Lists.newArrayListWithCapacity(upstreamList.size()); for (DivideUpstream divideUpstream : UpstreamList) {/ / based on the URL, IP + port agent live final Boolean pass = UpstreamCheckUtils. CheckUrl (divideUpstream. GetUpstreamUrl ()); if (pass) { if (! divideUpstream.isStatus()) { divideUpstream.setTimestamp(System.currentTimeMillis()); divideUpstream.setStatus(true); log.info("UpstreamCacheManager detect success the url: {}, host: {} ", divideUpstream.getUpstreamUrl(), divideUpstream.getUpstreamHost()); } resultList.add(divideUpstream); Divideuppage.setstatus (false);} else {// Set divideuppage.setStatus (false); log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl()); } } return resultList; }}Copy the code

UpstreamCheckUtils probe code analysis

conclusion

This article introduces the content of Divide plug-in load balancing and IP + port probing. Soul gateway Divide plug-in provides load balancing based on polling, hash, random three algorithms, and provides SPI mechanism to enable developers to extend load balancing; The Soul gateway Divide plug-in and soul-Admin provide IP + port-based exploration to update the list of viable services when a machine comes online or offline, enabling precise load balancing